mirror of
https://github.com/a-nyx/maputnik-with-pmtiles.git
synced 2024-12-26 20:20:40 +01:00
Added more webdriver tests testing against a real browser.
This commit is contained in:
parent
6e86c60f89
commit
942b2240a7
47 changed files with 2244 additions and 637 deletions
11
.babelrc
11
.babelrc
|
@ -1,4 +1,13 @@
|
|||
{
|
||||
"presets": ["env", "react"],
|
||||
"plugins": ["transform-object-rest-spread", "transform-class-properties"]
|
||||
"plugins": ["transform-object-rest-spread", "transform-class-properties"],
|
||||
"env": {
|
||||
"test": {
|
||||
"plugins": [
|
||||
["istanbul", {
|
||||
exclude: ["node_modules/**", "test/**"]
|
||||
}]
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
27
.circleci/config.yml
Normal file
27
.circleci/config.yml
Normal file
|
@ -0,0 +1,27 @@
|
|||
version: 2
|
||||
jobs:
|
||||
build:
|
||||
docker:
|
||||
# specify the version you desire here
|
||||
- image: node:8
|
||||
- image: selenium/standalone-chrome:3.1.0
|
||||
working_directory: ~/repo
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: "Create artifacts directory"
|
||||
command: mkdir /tmp/artifacts
|
||||
- restore_cache:
|
||||
key: v1-dependencies-{{ checksum "package.json" }}
|
||||
|
||||
- run: npm install
|
||||
|
||||
- save_cache:
|
||||
paths:
|
||||
- node_modules
|
||||
key: v1-dependencies-{{ checksum "package.json" }}
|
||||
|
||||
- run: mkdir -p /tmp/artifacts/logs
|
||||
- run: DOCKER_HOST=localhost npm test
|
||||
- run: npm run build
|
||||
- run: ./node_modules/.bin/istanbul report --include /tmp/artifacts/coverage/coverage.json --dir /tmp/artifacts/coverage html lcov
|
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -30,3 +30,5 @@ node_modules
|
|||
|
||||
# Ignore build files
|
||||
public
|
||||
/errorShots
|
||||
/old
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
machine:
|
||||
node:
|
||||
version: 6
|
||||
test:
|
||||
post:
|
||||
- npm run build
|
|
@ -2,36 +2,36 @@ var webpack = require("webpack");
|
|||
var WebpackDevServer = require("webpack-dev-server");
|
||||
var webpackConfig = require("./webpack.production.config");
|
||||
var testConfig = require("../test/config/specs");
|
||||
var artifacts = require("../test/artifacts");
|
||||
var isDocker = require("is-docker");
|
||||
|
||||
|
||||
var server;
|
||||
var SCREENSHOT_PATH = artifacts.pathSync("screenshots");
|
||||
|
||||
exports.config = {
|
||||
specs: [
|
||||
'./test/specs/**/*.js'
|
||||
'./test/functional/advanced.js'
|
||||
],
|
||||
exclude: [
|
||||
],
|
||||
maxInstances: 10,
|
||||
capabilities: [{
|
||||
maxInstances: 5,
|
||||
browserName: 'firefox'
|
||||
browserName: 'chrome'
|
||||
}],
|
||||
sync: true,
|
||||
logLevel: 'verbose',
|
||||
coloredLogs: true,
|
||||
bail: 0,
|
||||
screenshotPath: './errorShots/',
|
||||
screenshotPath: SCREENSHOT_PATH,
|
||||
host: (isDocker() ? process.env.DOCKER_HOST : "127.0.0.1"),
|
||||
baseUrl: 'http://localhost',
|
||||
waitforTimeout: 10000,
|
||||
connectionRetryTimeout: 90000,
|
||||
connectionRetryCount: 3,
|
||||
services: ['phantomjs'],
|
||||
framework: 'mocha',
|
||||
reporters: ['spec'],
|
||||
phantomjsOpts: {
|
||||
webdriverLogfile: 'phantomjs.log'
|
||||
},
|
||||
mochaOpts: {
|
||||
ui: 'bdd',
|
||||
// Because we don't know how long the initial build will take...
|
||||
|
@ -43,6 +43,6 @@ exports.config = {
|
|||
server.listen(testConfig.port);
|
||||
},
|
||||
onComplete: function(exitCode) {
|
||||
server.close();
|
||||
server.close()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
var webpack = require('webpack');
|
||||
var path = require('path');
|
||||
var loaders = require('./webpack.loaders');
|
||||
|
@ -7,14 +6,9 @@ var HtmlWebpackPlugin = require('html-webpack-plugin');
|
|||
var WebpackCleanupPlugin = require('webpack-cleanup-plugin');
|
||||
var BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
|
||||
var CopyWebpackPlugin = require('copy-webpack-plugin');
|
||||
var artifacts = require("../test/artifacts");
|
||||
|
||||
var OUTPATH;
|
||||
if(process.env.CIRCLE_ARTIFACTS) {
|
||||
OUTPATH = path.join(process.env.CIRCLE_ARTIFACTS, "build");
|
||||
}
|
||||
else {
|
||||
OUTPATH = path.join(__dirname, '..', 'public');
|
||||
}
|
||||
var OUTPATH = artifacts.pathSync("/build");
|
||||
|
||||
module.exports = {
|
||||
entry: {
|
||||
|
|
1177
package-lock.json
generated
1177
package-lock.json
generated
File diff suppressed because it is too large
Load diff
23
package.json
23
package.json
|
@ -39,14 +39,14 @@
|
|||
"ol-mapbox-style": "^1.0.1",
|
||||
"openlayers": "^4.4.2",
|
||||
"prop-types": "^15.6.0",
|
||||
"react": "16.0.0",
|
||||
"react": "^15.6.2",
|
||||
"react-addons-pure-render-mixin": "^15.6.2",
|
||||
"react-autocomplete": "^1.7.2",
|
||||
"react-codemirror2": "^3.0.7",
|
||||
"react-collapse": "^4.0.3",
|
||||
"react-color": "^2.13.8",
|
||||
"react-copy-to-clipboard": "^5.0.1",
|
||||
"react-dom": "16.0.0",
|
||||
"react-dom": "^15.6.2",
|
||||
"react-file-reader-input": "^1.1.4",
|
||||
"react-height": "^3.0.0",
|
||||
"react-icon-base": "^2.1.1",
|
||||
|
@ -90,6 +90,7 @@
|
|||
"babel-core": "^6.26.0",
|
||||
"babel-eslint": "^8.0.2",
|
||||
"babel-loader": "7.1.1",
|
||||
"babel-plugin-istanbul": "^4.1.5",
|
||||
"babel-plugin-transform-class-properties": "^6.24.1",
|
||||
"babel-plugin-transform-decorators-legacy": "^1.3.4",
|
||||
"babel-plugin-transform-flow-strip-types": "^6.22.0",
|
||||
|
@ -97,34 +98,38 @@
|
|||
"babel-plugin-transform-runtime": "^6.23.0",
|
||||
"babel-preset-env": "^1.6.1",
|
||||
"babel-preset-react": "^6.24.1",
|
||||
"babel-register": "^6.26.0",
|
||||
"babel-runtime": "^6.26.0",
|
||||
"base64-loader": "^1.0.0",
|
||||
"copy-webpack-plugin": "^4.2.0",
|
||||
"cors": "^2.8.4",
|
||||
"css-loader": "^0.28.7",
|
||||
"eslint": "^4.10.0",
|
||||
"eslint-plugin-react": "^7.4.0",
|
||||
"express": "^4.16.2",
|
||||
"extract-text-webpack-plugin": "^3.0.2",
|
||||
"file-loader": "^1.1.5",
|
||||
"html-webpack-plugin": "^2.30.1",
|
||||
"is-docker": "^1.1.0",
|
||||
"istanbul": "^0.4.5",
|
||||
"istanbul-lib-coverage": "^1.1.1",
|
||||
"json-loader": "^0.5.7",
|
||||
"karma": "^1.7.1",
|
||||
"karma-chrome-launcher": "^2.2.0",
|
||||
"karma-firefox-launcher": "^1.0.1",
|
||||
"karma-mocha": "^1.3.0",
|
||||
"karma-webpack": "^2.0.5",
|
||||
"mkdirp": "^0.5.1",
|
||||
"mocha": "^4.0.1",
|
||||
"mocha-loader": "^1.1.1",
|
||||
"node-sass": "^4.6.0",
|
||||
"react-hot-loader": "^3.1.1",
|
||||
"sass-loader": "^6.0.6",
|
||||
"selenium-standalone": "^6.11.0",
|
||||
"style-loader": "^0.19.0",
|
||||
"stylelint": "^7.13.0",
|
||||
"stylelint-config-standard": "^15.0.1",
|
||||
"transform-loader": "^0.2.4",
|
||||
"uuid": "^3.1.0",
|
||||
"wdio-mocha-framework": "^0.5.11",
|
||||
"wdio-phantomjs-service": "^0.2.2",
|
||||
"wdio-selenium-standalone-service": "0.0.9",
|
||||
"wdio-spec-reporter": "^0.1.2",
|
||||
"webdriverio": "^4.8.0",
|
||||
"webdriverio": "^4.9.8",
|
||||
"webpack": "^3.8.1",
|
||||
"webpack-bundle-analyzer": "^2.9.0",
|
||||
"webpack-cleanup-plugin": "^0.5.1",
|
||||
|
|
|
@ -20,6 +20,7 @@ import { RevisionStore } from '../libs/revisions'
|
|||
import LayerWatcher from '../libs/layerwatcher'
|
||||
import tokens from '../config/tokens.json'
|
||||
import isEqual from 'lodash.isequal'
|
||||
import Debug from '../libs/debug'
|
||||
|
||||
function updateRootSpec(spec, fieldName, newValues) {
|
||||
return {
|
||||
|
@ -53,9 +54,19 @@ export default class App extends React.Component {
|
|||
this.styleStore = new StyleStore()
|
||||
}
|
||||
this.styleStore.latestStyle(mapStyle => this.onStyleChanged(mapStyle))
|
||||
|
||||
if(Debug.enabled()) {
|
||||
Debug.set("maputnik", "styleStore", this.styleStore);
|
||||
Debug.set("maputnik", "revisionStore", this.revisionStore);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if(Debug.enabled()) {
|
||||
Debug.set("maputnik", "revisionStore", this.revisionStore);
|
||||
Debug.set("maputnik", "styleStore", this.styleStore);
|
||||
}
|
||||
|
||||
this.state = {
|
||||
errors: [],
|
||||
infos: [],
|
||||
|
@ -70,22 +81,28 @@ export default class App extends React.Component {
|
|||
this.layerWatcher = new LayerWatcher({
|
||||
onVectorLayersChange: v => this.setState({ vectorLayers: v })
|
||||
})
|
||||
|
||||
this.onKeyDown = this.onKeyDown.bind(this);
|
||||
}
|
||||
|
||||
onKeyDown(e) {
|
||||
console.log("??? keyCode ctrlKey="+e.ctrlKey+", keyCode="+e.keyCode)
|
||||
// Control + Z
|
||||
if(e.ctrlKey && e.keyCode === 90) {
|
||||
this.onUndo(e);
|
||||
}
|
||||
else if(e.ctrlKey && e.keyCode === 89) {
|
||||
this.onRedo(e);
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.fetchSources();
|
||||
Mousetrap.bind(['ctrl+z'], this.onUndo.bind(this));
|
||||
Mousetrap.bind(['ctrl+y'], this.onRedo.bind(this));
|
||||
document.addEventListener("keydown", this.onKeyDown);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
Mousetrap.unbind(['ctrl+z'], this.onUndo.bind(this));
|
||||
Mousetrap.unbind(['ctrl+y'], this.onRedo.bind(this));
|
||||
}
|
||||
|
||||
onReset() {
|
||||
this.styleStore.purge()
|
||||
loadDefaultStyle(mapStyle => this.onStyleOpen(mapStyle))
|
||||
document.removeEventListener("keydown", this.onKeyDown);
|
||||
}
|
||||
|
||||
saveStyle(snapshotStyle) {
|
||||
|
|
|
@ -4,6 +4,7 @@ import classnames from 'classnames'
|
|||
|
||||
class Button extends React.Component {
|
||||
static propTypes = {
|
||||
"data-wd-key": PropTypes.string,
|
||||
onClick: PropTypes.func,
|
||||
style: PropTypes.object,
|
||||
className: PropTypes.string,
|
||||
|
@ -14,6 +15,7 @@ class Button extends React.Component {
|
|||
return <a
|
||||
onClick={this.props.onClick}
|
||||
className={classnames("maputnik-button", this.props.className)}
|
||||
data-wd-key={this.props["data-wd-key"]}
|
||||
style={this.props.style}>
|
||||
{this.props.children}
|
||||
</a>
|
||||
|
|
|
@ -63,6 +63,7 @@ class ToolbarAction extends React.Component {
|
|||
render() {
|
||||
return <a
|
||||
className='maputnik-toolbar-action'
|
||||
data-wd-key={this.props.wdKey}
|
||||
onClick={this.props.onClick}
|
||||
>
|
||||
{this.props.children}
|
||||
|
@ -139,23 +140,23 @@ export default class Toolbar extends React.Component {
|
|||
<h1>Maputnik</h1>
|
||||
</ToolbarLink>
|
||||
<div className="maputnik-toolbar__actions">
|
||||
<ToolbarAction onClick={this.toggleModal.bind(this, 'open')}>
|
||||
<ToolbarAction wdKey="nav:open" onClick={this.toggleModal.bind(this, 'open')}>
|
||||
<OpenIcon />
|
||||
<IconText>Open</IconText>
|
||||
</ToolbarAction>
|
||||
<ToolbarAction onClick={this.toggleModal.bind(this, 'export')}>
|
||||
<ToolbarAction wdKey="nav:export" onClick={this.toggleModal.bind(this, 'export')}>
|
||||
<MdFileDownload />
|
||||
<IconText>Export</IconText>
|
||||
</ToolbarAction>
|
||||
<ToolbarAction onClick={this.toggleModal.bind(this, 'sources')}>
|
||||
<ToolbarAction wdKey="nav:sources" onClick={this.toggleModal.bind(this, 'sources')}>
|
||||
<SourcesIcon />
|
||||
<IconText>Sources</IconText>
|
||||
</ToolbarAction>
|
||||
<ToolbarAction onClick={this.toggleModal.bind(this, 'settings')}>
|
||||
<ToolbarAction wdKey="nav:settings" onClick={this.toggleModal.bind(this, 'settings')}>
|
||||
<SettingsIcon />
|
||||
<IconText>Style Settings</IconText>
|
||||
</ToolbarAction>
|
||||
<ToolbarAction onClick={this.props.onInspectModeToggle}>
|
||||
<ToolbarAction wdKey="nav:inspect" onClick={this.props.onInspectModeToggle}>
|
||||
<InspectionIcon />
|
||||
<IconText>
|
||||
{ this.props.inspectModeEnabled && <span>Map Mode</span> }
|
||||
|
|
|
@ -131,8 +131,7 @@ export default class FunctionSpecProperty extends React.Component {
|
|||
/>
|
||||
)
|
||||
}
|
||||
|
||||
return <div className={propClass}>
|
||||
return <div className={propClass} data-wd-key={"spec-field:"+this.props.fieldName}>
|
||||
{specField}
|
||||
</div>
|
||||
}
|
||||
|
|
|
@ -58,6 +58,8 @@ export default class SpecField extends React.Component {
|
|||
name: this.props.fieldName,
|
||||
onChange: newValue => this.props.onChange(this.props.fieldName, newValue)
|
||||
}
|
||||
|
||||
function childNodes() {
|
||||
switch(this.props.fieldSpec.type) {
|
||||
case 'number': return (
|
||||
<NumberInput
|
||||
|
@ -124,4 +126,11 @@ export default class SpecField extends React.Component {
|
|||
default: return null
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div data-wd-key={"spec-field:"+this.props.fieldName}>
|
||||
{childNodes.call(this)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -89,7 +89,7 @@ export default class CombiningFilterEditor extends React.Component {
|
|||
}
|
||||
|
||||
return <div className="maputnik-filter-editor">
|
||||
<div className="maputnik-filter-editor-compound-select">
|
||||
<div className="maputnik-filter-editor-compound-select" data-wd-key="layer-filter">
|
||||
<DocLabel
|
||||
label={"Compound Filter"}
|
||||
doc={styleSpec.latest.layer.filter.doc + " Combine multiple filters together by using a compound filter."}
|
||||
|
@ -103,6 +103,7 @@ export default class CombiningFilterEditor extends React.Component {
|
|||
{editorBlocks}
|
||||
<div className="maputnik-filter-editor-add-wrapper">
|
||||
<Button
|
||||
data-wd-key="layer-filter-button"
|
||||
className="maputnik-add-filter"
|
||||
onClick={this.addFilterItem.bind(this)}>
|
||||
Add filter
|
||||
|
|
|
@ -23,7 +23,6 @@ class LayerIcon extends React.Component {
|
|||
case 'line': return <LineIcon {...iconProps} />
|
||||
case 'symbol': return <SymbolIcon {...iconProps} />
|
||||
case 'circle': return <CircleIcon {...iconProps} />
|
||||
default: return null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -94,8 +94,14 @@ class AutocompleteInput extends React.Component {
|
|||
value={this.props.value}
|
||||
items={this.props.options}
|
||||
getItemValue={(item) => item[0]}
|
||||
onSelect={v => this.props.onChange(v)}
|
||||
onChange={(e, v) => this.props.onChange(v)}
|
||||
onSelect={v => {
|
||||
console.log("@@ onSelect", v)
|
||||
this.props.onChange(v)
|
||||
}}
|
||||
onChange={(e, v) => {
|
||||
console.log("@@ onChange", v)
|
||||
this.props.onChange(v)
|
||||
}}
|
||||
renderItem={(item, isHighlighted) => (
|
||||
<div
|
||||
key={item[0]}
|
||||
|
|
|
@ -6,6 +6,7 @@ import DocLabel from '../fields/DocLabel'
|
|||
/** Wrap a component with a label */
|
||||
class InputBlock extends React.Component {
|
||||
static propTypes = {
|
||||
"data-wd-key": PropTypes.string,
|
||||
label: PropTypes.oneOfType([
|
||||
PropTypes.string,
|
||||
PropTypes.element,
|
||||
|
@ -24,6 +25,7 @@ class InputBlock extends React.Component {
|
|||
|
||||
render() {
|
||||
return <div style={this.props.style}
|
||||
data-wd-key={this.props["data-wd-key"]}
|
||||
className={classnames({
|
||||
"maputnik-input-block": true,
|
||||
"maputnik-action-block": this.props.action
|
||||
|
|
|
@ -4,6 +4,7 @@ import PropTypes from 'prop-types'
|
|||
class SelectInput extends React.Component {
|
||||
static propTypes = {
|
||||
value: PropTypes.string.isRequired,
|
||||
"data-wd-key": PropTypes.string.isRequired,
|
||||
options: PropTypes.array.isRequired,
|
||||
style: PropTypes.object,
|
||||
onChange: PropTypes.func.isRequired,
|
||||
|
@ -18,6 +19,7 @@ class SelectInput extends React.Component {
|
|||
|
||||
return <select
|
||||
className="maputnik-select"
|
||||
data-wd-key={this.props["data-wd-key"]}
|
||||
style={this.props.style}
|
||||
value={this.props.value}
|
||||
onChange={e => this.props.onChange(e.target.value)}
|
||||
|
|
|
@ -3,6 +3,7 @@ import PropTypes from 'prop-types'
|
|||
|
||||
class StringInput extends React.Component {
|
||||
static propTypes = {
|
||||
"data-wd-key": PropTypes.string,
|
||||
value: PropTypes.string,
|
||||
style: PropTypes.object,
|
||||
default: PropTypes.string,
|
||||
|
@ -18,6 +19,7 @@ class StringInput extends React.Component {
|
|||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
console.log("@@ STRING componentWillReceiveProps", JSON.stringify(nextProps))
|
||||
this.setState({ value: nextProps.value || '' })
|
||||
}
|
||||
|
||||
|
@ -40,12 +42,19 @@ class StringInput extends React.Component {
|
|||
}
|
||||
|
||||
return React.createElement(tag, {
|
||||
"data-wd-key": this.props["data-wd-key"],
|
||||
className: classes.join(" "),
|
||||
style: this.props.style,
|
||||
value: this.state.value,
|
||||
placeholder: this.props.default,
|
||||
onChange: e => this.setState({ value: e.target.value }),
|
||||
onChange: e => {
|
||||
console.log("@@ STRING CHANGE", JSON.stringify(e.target.value));
|
||||
this.setState({
|
||||
value: e.target.value
|
||||
})
|
||||
},
|
||||
onBlur: () => {
|
||||
console.log("@@ STRING BLUR", JSON.stringify(this.state.value), "props:", JSON.stringify(this.props.value));
|
||||
if(this.state.value!==this.props.value) this.props.onChange(this.state.value)
|
||||
}
|
||||
});
|
||||
|
|
|
@ -11,7 +11,11 @@ class MetadataBlock extends React.Component {
|
|||
}
|
||||
|
||||
render() {
|
||||
return <InputBlock label={"Comments"} doc={"Comments for the current layer. This is non-standard and not in the spec."}>
|
||||
return <InputBlock
|
||||
label={"Comments"}
|
||||
doc={"Comments for the current layer. This is non-standard and not in the spec."}
|
||||
data-wd-key="layer-comment"
|
||||
>
|
||||
<StringInput
|
||||
multi={true}
|
||||
value={this.props.value}
|
||||
|
|
|
@ -4,7 +4,6 @@ import PropTypes from 'prop-types'
|
|||
import {Controlled as CodeMirror} from 'react-codemirror2'
|
||||
import InputBlock from '../inputs/InputBlock'
|
||||
import StringInput from '../inputs/StringInput'
|
||||
import SelectInput from '../inputs/SelectInput'
|
||||
|
||||
import 'codemirror/mode/javascript/javascript'
|
||||
import 'codemirror/addon/lint/lint'
|
||||
|
|
|
@ -19,11 +19,6 @@ import MultiButtonInput from '../inputs/MultiButtonInput'
|
|||
import { changeType, changeProperty } from '../../libs/layer'
|
||||
import layout from '../../config/layout.json'
|
||||
|
||||
class UnsupportedLayer extends React.Component {
|
||||
render() {
|
||||
return <div></div>
|
||||
}
|
||||
}
|
||||
|
||||
function layoutGroups(layerType) {
|
||||
const layerGroup = {
|
||||
|
@ -121,6 +116,7 @@ export default class LayerEditor extends React.Component {
|
|||
case 'layer': return <div>
|
||||
<LayerIdBlock
|
||||
value={this.props.layer.id}
|
||||
wdKey="layer-editor.layer-id"
|
||||
onChange={newId => this.props.onLayerIdChange(this.props.layer.id, newId)}
|
||||
/>
|
||||
<LayerTypeBlock
|
||||
|
@ -171,7 +167,6 @@ export default class LayerEditor extends React.Component {
|
|||
layer={this.props.layer}
|
||||
onChange={this.props.onLayerChanged}
|
||||
/>
|
||||
default: return null
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -181,6 +176,7 @@ export default class LayerEditor extends React.Component {
|
|||
return !(layerType === 'background' && group.type === 'source')
|
||||
}).map(group => {
|
||||
return <LayerEditorGroup
|
||||
data-wd-key={group.title}
|
||||
key={group.title}
|
||||
title={group.title}
|
||||
isActive={this.state.editorGroups[group.title]}
|
||||
|
|
|
@ -5,6 +5,7 @@ import Collapser from './Collapser'
|
|||
|
||||
export default class LayerEditorGroup extends React.Component {
|
||||
static propTypes = {
|
||||
"data-wd-key": PropTypes.string,
|
||||
title: PropTypes.string.isRequired,
|
||||
isActive: PropTypes.bool.isRequired,
|
||||
children: PropTypes.element.isRequired,
|
||||
|
@ -14,6 +15,7 @@ export default class LayerEditorGroup extends React.Component {
|
|||
render() {
|
||||
return <div>
|
||||
<div className="maputnik-layer-editor-group"
|
||||
data-wd-key={"layer-editor-group:"+this.props["data-wd-key"]}
|
||||
onClick={e => this.props.onActiveToggle(!this.props.isActive)}
|
||||
>
|
||||
<span>{this.props.title}</span>
|
||||
|
|
|
@ -8,11 +8,14 @@ import StringInput from '../inputs/StringInput'
|
|||
class LayerIdBlock extends React.Component {
|
||||
static propTypes = {
|
||||
value: PropTypes.string.isRequired,
|
||||
wdKey: PropTypes.string.isRequired,
|
||||
onChange: PropTypes.func.isRequired,
|
||||
}
|
||||
|
||||
render() {
|
||||
return <InputBlock label={"ID"} doc={styleSpec.latest.layer.id.doc}>
|
||||
return <InputBlock label={"ID"} doc={styleSpec.latest.layer.id.doc}
|
||||
data-wd-key={this.props.wdKey}
|
||||
>
|
||||
<StringInput
|
||||
value={this.props.value}
|
||||
onChange={this.props.onChange}
|
||||
|
|
|
@ -162,6 +162,7 @@ class LayerListContainer extends React.Component {
|
|||
const groupPrefix = layerPrefix(layers[0].id)
|
||||
if(layers.length > 1) {
|
||||
const grp = <LayerListGroup
|
||||
data-wd-key={[groupPrefix, idx].join('-')}
|
||||
key={[groupPrefix, idx].join('-')}
|
||||
title={groupPrefix}
|
||||
isActive={!this.isCollapsed(groupPrefix, idx)}
|
||||
|
@ -217,6 +218,7 @@ class LayerListContainer extends React.Component {
|
|||
<div className="maputnik-multibutton">
|
||||
<a
|
||||
onClick={this.toggleModal.bind(this, 'add')}
|
||||
data-wd-key="layer-list:add-layer"
|
||||
className="maputnik-button maputnik-button-selected">
|
||||
Add Layer
|
||||
</a>
|
||||
|
|
|
@ -5,6 +5,7 @@ import Collapser from './Collapser'
|
|||
export default class LayerListGroup extends React.Component {
|
||||
static propTypes = {
|
||||
title: PropTypes.string.isRequired,
|
||||
"data-wd-key": PropTypes.string,
|
||||
isActive: PropTypes.bool.isRequired,
|
||||
onActiveToggle: PropTypes.func.isRequired
|
||||
}
|
||||
|
@ -12,6 +13,7 @@ export default class LayerListGroup extends React.Component {
|
|||
render() {
|
||||
return <li className="maputnik-layer-list-group">
|
||||
<div className="maputnik-layer-list-group-header"
|
||||
data-wd-key={"layer-list-group:"+this.props["data-wd-key"]}
|
||||
onClick={e => this.props.onActiveToggle(!this.props.isActive)}
|
||||
>
|
||||
<span className="maputnik-layer-list-group-title">{this.props.title}</span>
|
||||
|
|
|
@ -33,6 +33,7 @@ class IconAction extends React.Component {
|
|||
static propTypes = {
|
||||
action: PropTypes.string.isRequired,
|
||||
onClick: PropTypes.func.isRequired,
|
||||
wdKey: PropTypes.string
|
||||
}
|
||||
|
||||
renderIcon() {
|
||||
|
@ -41,13 +42,13 @@ class IconAction extends React.Component {
|
|||
case 'show': return <VisibilityIcon />
|
||||
case 'hide': return <VisibilityOffIcon />
|
||||
case 'delete': return <DeleteIcon />
|
||||
default: return null
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return <a
|
||||
className="maputnik-layer-list-icon-action"
|
||||
data-wd-key={this.props.wdKey}
|
||||
onClick={this.props.onClick}
|
||||
>
|
||||
{this.renderIcon()}
|
||||
|
@ -92,6 +93,7 @@ class LayerListItem extends React.Component {
|
|||
return <li
|
||||
key={this.props.layerId}
|
||||
onClick={e => this.props.onLayerSelect(this.props.layerId)}
|
||||
data-wd-key={"layer-list-item:"+this.props.layerId}
|
||||
className={classnames({
|
||||
"maputnik-layer-list-item": true,
|
||||
"maputnik-layer-list-item-selected": this.props.isSelected,
|
||||
|
@ -101,14 +103,17 @@ class LayerListItem extends React.Component {
|
|||
<span className="maputnik-layer-list-item-id">{this.props.layerId}</span>
|
||||
<span style={{flexGrow: 1}} />
|
||||
<IconAction
|
||||
wdKey={"layer-list-item:"+this.props.layerId+":delete"}
|
||||
action={'delete'}
|
||||
onClick={e => this.props.onLayerDestroy(this.props.layerId)}
|
||||
/>
|
||||
<IconAction
|
||||
wdKey={"layer-list-item:"+this.props.layerId+":copy"}
|
||||
action={'copy'}
|
||||
onClick={e => this.props.onLayerCopy(this.props.layerId)}
|
||||
/>
|
||||
<IconAction
|
||||
wdKey={"layer-list-item:"+this.props.layerId+":toggle-visibility"}
|
||||
action={this.props.visibility === 'visible' ? 'hide' : 'show'}
|
||||
onClick={e => this.props.onLayerVisibilityToggle(this.props.layerId)}
|
||||
/>
|
||||
|
|
|
@ -4,12 +4,12 @@ import PropTypes from 'prop-types'
|
|||
import styleSpec from '@mapbox/mapbox-gl-style-spec/style-spec'
|
||||
import InputBlock from '../inputs/InputBlock'
|
||||
import StringInput from '../inputs/StringInput'
|
||||
import SelectInput from '../inputs/SelectInput'
|
||||
import AutocompleteInput from '../inputs/AutocompleteInput'
|
||||
|
||||
class LayerSourceBlock extends React.Component {
|
||||
static propTypes = {
|
||||
value: PropTypes.string,
|
||||
wdKey: PropTypes.string,
|
||||
onChange: PropTypes.func,
|
||||
sourceIds: PropTypes.array,
|
||||
}
|
||||
|
@ -20,7 +20,9 @@ class LayerSourceBlock extends React.Component {
|
|||
}
|
||||
|
||||
render() {
|
||||
return <InputBlock label={"Source"} doc={styleSpec.latest.layer.source.doc}>
|
||||
return <InputBlock label={"Source"} doc={styleSpec.latest.layer.source.doc}
|
||||
data-wd-key={this.props.wdKey}
|
||||
>
|
||||
<AutocompleteInput
|
||||
value={this.props.value}
|
||||
onChange={this.props.onChange}
|
||||
|
|
|
@ -4,7 +4,6 @@ import PropTypes from 'prop-types'
|
|||
import styleSpec from '@mapbox/mapbox-gl-style-spec/style-spec'
|
||||
import InputBlock from '../inputs/InputBlock'
|
||||
import StringInput from '../inputs/StringInput'
|
||||
import SelectInput from '../inputs/SelectInput'
|
||||
import AutocompleteInput from '../inputs/AutocompleteInput'
|
||||
|
||||
class LayerSourceLayer extends React.Component {
|
||||
|
@ -22,7 +21,9 @@ class LayerSourceLayer extends React.Component {
|
|||
}
|
||||
|
||||
render() {
|
||||
return <InputBlock label={"Source Layer"} doc={styleSpec.latest.layer['source-layer'].doc}>
|
||||
return <InputBlock label={"Source Layer"} doc={styleSpec.latest.layer['source-layer'].doc}
|
||||
data-wd-key="layer-source-layer"
|
||||
>
|
||||
<AutocompleteInput
|
||||
keepMenuWithinWindowBounds={!!this.props.isFixed}
|
||||
value={this.props.value}
|
||||
|
|
|
@ -8,11 +8,14 @@ import SelectInput from '../inputs/SelectInput'
|
|||
class LayerTypeBlock extends React.Component {
|
||||
static propTypes = {
|
||||
value: PropTypes.string.isRequired,
|
||||
wdKey: PropTypes.string.isRequired,
|
||||
onChange: PropTypes.func.isRequired,
|
||||
}
|
||||
|
||||
render() {
|
||||
return <InputBlock label={"Type"} doc={styleSpec.latest.layer.type.doc}>
|
||||
return <InputBlock label={"Type"} doc={styleSpec.latest.layer.type.doc}
|
||||
data-wd-key={this.props.wdKey}
|
||||
>
|
||||
<SelectInput
|
||||
options={[
|
||||
['background', 'Background'],
|
||||
|
|
|
@ -12,7 +12,9 @@ class MaxZoomBlock extends React.Component {
|
|||
}
|
||||
|
||||
render() {
|
||||
return <InputBlock label={"Max Zoom"} doc={styleSpec.latest.layer.maxzoom.doc}>
|
||||
return <InputBlock label={"Max Zoom"} doc={styleSpec.latest.layer.maxzoom.doc}
|
||||
data-wd-key="max-zoom"
|
||||
>
|
||||
<NumberInput
|
||||
value={this.props.value}
|
||||
onChange={this.props.onChange}
|
||||
|
|
|
@ -12,7 +12,9 @@ class MinZoomBlock extends React.Component {
|
|||
}
|
||||
|
||||
render() {
|
||||
return <InputBlock label={"Min Zoom"} doc={styleSpec.latest.layer.minzoom.doc}>
|
||||
return <InputBlock label={"Min Zoom"} doc={styleSpec.latest.layer.minzoom.doc}
|
||||
data-wd-key="min-zoom"
|
||||
>
|
||||
<NumberInput
|
||||
value={this.props.value}
|
||||
onChange={this.props.onChange}
|
||||
|
|
|
@ -4,7 +4,6 @@ import PropTypes from 'prop-types'
|
|||
import Button from '../Button'
|
||||
import InputBlock from '../inputs/InputBlock'
|
||||
import StringInput from '../inputs/StringInput'
|
||||
import SelectInput from '../inputs/SelectInput'
|
||||
import Modal from './Modal'
|
||||
|
||||
import LayerTypeBlock from '../layers/LayerTypeBlock'
|
||||
|
@ -108,19 +107,26 @@ class AddModal extends React.Component {
|
|||
isOpen={this.props.isOpen}
|
||||
onOpenToggle={this.props.onOpenToggle}
|
||||
title={'Add Layer'}
|
||||
data-wd-key="modal:add-layer"
|
||||
>
|
||||
<div className="maputnik-add-layer">
|
||||
<LayerIdBlock
|
||||
value={this.state.id}
|
||||
onChange={v => this.setState({ id: v })}
|
||||
wdKey="add-layer.layer-id"
|
||||
onChange={v => {
|
||||
console.log("@@ upper_id_change", JSON.stringify(v))
|
||||
this.setState({ id: v })
|
||||
}}
|
||||
/>
|
||||
<LayerTypeBlock
|
||||
value={this.state.type}
|
||||
wdKey="add-layer.layer-type"
|
||||
onChange={v => this.setState({ type: v })}
|
||||
/>
|
||||
{this.state.type !== 'background' &&
|
||||
<LayerSourceBlock
|
||||
sourceIds={sources}
|
||||
wdKey="add-layer.layer-source-block"
|
||||
value={this.state.source}
|
||||
onChange={v => this.setState({ source: v })}
|
||||
/>
|
||||
|
@ -133,7 +139,11 @@ class AddModal extends React.Component {
|
|||
onChange={v => this.setState({ 'source-layer': v })}
|
||||
/>
|
||||
}
|
||||
<Button className="maputnik-add-layer-button" onClick={this.addLayer.bind(this)}>
|
||||
<Button
|
||||
className="maputnik-add-layer-button"
|
||||
onClick={this.addLayer.bind(this)}
|
||||
data-wd-key="add-layer"
|
||||
>
|
||||
Add Layer
|
||||
</Button>
|
||||
</div>
|
||||
|
|
|
@ -5,7 +5,6 @@ import { saveAs } from 'file-saver'
|
|||
import styleSpec from '@mapbox/mapbox-gl-style-spec/style-spec'
|
||||
import InputBlock from '../inputs/InputBlock'
|
||||
import StringInput from '../inputs/StringInput'
|
||||
import SelectInput from '../inputs/SelectInput'
|
||||
import CheckboxInput from '../inputs/CheckboxInput'
|
||||
import Button from '../Button'
|
||||
import Modal from './Modal'
|
||||
|
@ -215,6 +214,7 @@ class ExportModal extends React.Component {
|
|||
|
||||
render() {
|
||||
return <Modal
|
||||
data-wd-key="export-modal"
|
||||
isOpen={this.props.isOpen}
|
||||
onOpenToggle={this.props.onOpenToggle}
|
||||
title={'Export Style'}
|
||||
|
|
|
@ -5,6 +5,7 @@ import Overlay from './Overlay'
|
|||
|
||||
class Modal extends React.Component {
|
||||
static propTypes = {
|
||||
"data-wd-key": PropTypes.string,
|
||||
isOpen: PropTypes.bool.isRequired,
|
||||
title: PropTypes.string.isRequired,
|
||||
onOpenToggle: PropTypes.func.isRequired,
|
||||
|
@ -13,12 +14,15 @@ class Modal extends React.Component {
|
|||
|
||||
render() {
|
||||
return <Overlay isOpen={this.props.isOpen}>
|
||||
<div className="maputnik-modal">
|
||||
<div className="maputnik-modal"
|
||||
data-wd-key={this.props["data-wd-key"]}
|
||||
>
|
||||
<header className="maputnik-modal-header">
|
||||
<h1 className="maputnik-modal-header-title">{this.props.title}</h1>
|
||||
<span className="maputnik-modal-header-space"></span>
|
||||
<a className="maputnik-modal-header-toggle"
|
||||
onClick={() => this.props.onOpenToggle(false)}
|
||||
data-wd-key={this.props["data-wd-key"]+".close-modal"}
|
||||
>
|
||||
<CloseIcon />
|
||||
</a>
|
||||
|
|
|
@ -133,6 +133,7 @@ class OpenModal extends React.Component {
|
|||
}
|
||||
|
||||
return <Modal
|
||||
data-wd-key="open-modal"
|
||||
isOpen={this.props.isOpen}
|
||||
onOpenToggle={() => this.onOpenToggle()}
|
||||
title={'Open Style'}
|
||||
|
@ -151,9 +152,9 @@ class OpenModal extends React.Component {
|
|||
<p>
|
||||
Load from a URL. Note that the URL must have <a href="https://enable-cors.org" target="_blank" rel="noopener noreferrer">CORS enabled</a>.
|
||||
</p>
|
||||
<input type="text" ref={(input) => this.styleUrlElement = input} className="maputnik-input" placeholder="Enter URL..."/>
|
||||
<input data-wd-key="open-modal.url.input" type="text" ref={(input) => this.styleUrlElement = input} className="maputnik-input" placeholder="Enter URL..."/>
|
||||
<div>
|
||||
<Button className="maputnik-big-button" onClick={this.onOpenUrl.bind(this)}>Open URL</Button>
|
||||
<Button data-wd-key="open-modal.url.button" className="maputnik-big-button" onClick={this.onOpenUrl.bind(this)}>Open URL</Button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
|
|
@ -42,6 +42,7 @@ class SettingsModal extends React.Component {
|
|||
const metadata = this.props.mapStyle.metadata || {}
|
||||
const inputProps = { }
|
||||
return <Modal
|
||||
data-wd-key="modal-settings"
|
||||
isOpen={this.props.isOpen}
|
||||
onOpenToggle={this.props.onOpenToggle}
|
||||
title={'Style Settings'}
|
||||
|
@ -49,18 +50,21 @@ class SettingsModal extends React.Component {
|
|||
<div style={{minWidth: 350}}>
|
||||
<InputBlock label={"Name"} doc={styleSpec.latest.$root.name.doc}>
|
||||
<StringInput {...inputProps}
|
||||
data-wd-key="modal-settings.name"
|
||||
value={this.props.mapStyle.name}
|
||||
onChange={this.changeStyleProperty.bind(this, "name")}
|
||||
/>
|
||||
</InputBlock>
|
||||
<InputBlock label={"Owner"} doc={"Owner ID of the style. Used by Mapbox or future style APIs."}>
|
||||
<StringInput {...inputProps}
|
||||
data-wd-key="modal-settings.owner"
|
||||
value={this.props.mapStyle.owner}
|
||||
onChange={this.changeStyleProperty.bind(this, "owner")}
|
||||
/>
|
||||
</InputBlock>
|
||||
<InputBlock label={"Sprite URL"} doc={styleSpec.latest.$root.sprite.doc}>
|
||||
<StringInput {...inputProps}
|
||||
data-wd-key="modal-settings.sprite"
|
||||
value={this.props.mapStyle.sprite}
|
||||
onChange={this.changeStyleProperty.bind(this, "sprite")}
|
||||
/>
|
||||
|
@ -68,6 +72,7 @@ class SettingsModal extends React.Component {
|
|||
|
||||
<InputBlock label={"Glyphs URL"} doc={styleSpec.latest.$root.glyphs.doc}>
|
||||
<StringInput {...inputProps}
|
||||
data-wd-key="modal-settings.glyphs"
|
||||
value={this.props.mapStyle.glyphs}
|
||||
onChange={this.changeStyleProperty.bind(this, "glyphs")}
|
||||
/>
|
||||
|
@ -75,6 +80,7 @@ class SettingsModal extends React.Component {
|
|||
|
||||
<InputBlock label={"Mapbox Access Token"} doc={"Public access token for Mapbox services."}>
|
||||
<StringInput {...inputProps}
|
||||
data-wd-key="modal-settings.maputnik:mapbox_access_token"
|
||||
value={metadata['maputnik:mapbox_access_token']}
|
||||
onChange={this.changeMetadataProperty.bind(this, "maputnik:mapbox_access_token")}
|
||||
/>
|
||||
|
@ -82,6 +88,7 @@ class SettingsModal extends React.Component {
|
|||
|
||||
<InputBlock label={"OpenMapTiles Access Token"} doc={"Public access token for the OpenMapTiles CDN."}>
|
||||
<StringInput {...inputProps}
|
||||
data-wd-key="modal-settings.maputnik:openmaptiles_access_token"
|
||||
value={metadata['maputnik:openmaptiles_access_token']}
|
||||
onChange={this.changeMetadataProperty.bind(this, "maputnik:openmaptiles_access_token")}
|
||||
/>
|
||||
|
@ -89,6 +96,7 @@ class SettingsModal extends React.Component {
|
|||
|
||||
<InputBlock label={"Style Renderer"} doc={"Choose the default Maputnik renderer for this style."}>
|
||||
<SelectInput {...inputProps}
|
||||
data-wd-key="modal-settings.maputnik:renderer"
|
||||
options={[
|
||||
['mbgljs', 'MapboxGL JS'],
|
||||
['ol3', 'Open Layers 3'],
|
||||
|
|
44
src/libs/debug.js
Normal file
44
src/libs/debug.js
Normal file
|
@ -0,0 +1,44 @@
|
|||
import querystring from 'querystring'
|
||||
|
||||
|
||||
const debugStore = {};
|
||||
|
||||
function enabled() {
|
||||
const qs = querystring.parse(window.location.search.slice(1));
|
||||
if(qs.hasOwnProperty("debug")) {
|
||||
return !!qs.debug.match(/^(|1|true)$/);
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function genErr() {
|
||||
return new Error("Debug not enabled, enable by appending '?debug' to your query string");
|
||||
}
|
||||
|
||||
function set(namespace, key, value) {
|
||||
if(!enabled()) {
|
||||
throw genErr();
|
||||
}
|
||||
debugStore[namespace] = debugStore[namespace] || {};
|
||||
debugStore[namespace][key] = value;
|
||||
}
|
||||
|
||||
function get(namespace, key) {
|
||||
if(!enabled()) {
|
||||
throw genErr();
|
||||
}
|
||||
if(debugStore.hasOwnProperty(namespace)) {
|
||||
return debugStore[namespace][key];
|
||||
}
|
||||
}
|
||||
|
||||
const mod = {
|
||||
enabled,
|
||||
get,
|
||||
set
|
||||
}
|
||||
|
||||
window.debug = mod;
|
||||
export default mod;
|
|
@ -36,3 +36,15 @@ $toolbar-offset: 0;
|
|||
@import 'zoomproperty';
|
||||
@import 'popup';
|
||||
@import 'map';
|
||||
|
||||
|
||||
/**
|
||||
* Hacks for webdriverio isVisibleWithinViewport
|
||||
*/
|
||||
#app {
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.maputnik-layout {
|
||||
height: 100vh;
|
||||
}
|
||||
|
|
39
test/artifacts.js
Normal file
39
test/artifacts.js
Normal file
|
@ -0,0 +1,39 @@
|
|||
var path = require("path");
|
||||
var mkdirp = require("mkdirp");
|
||||
|
||||
|
||||
function genPath(subPath) {
|
||||
subPath = subPath || ".";
|
||||
var buildPath;
|
||||
|
||||
if(process.env.CIRCLE_ARTIFACTS) {
|
||||
buildPath = path.join(process.env.CIRCLE_ARTIFACTS, subPath);
|
||||
}
|
||||
else {
|
||||
buildPath = path.join(__dirname, '..', 'build', subPath);
|
||||
}
|
||||
|
||||
return buildPath;
|
||||
}
|
||||
|
||||
module.exports.path = function(subPath) {
|
||||
var dirPath = genPath(subPath);
|
||||
|
||||
return new Promise(function(resolve, reject) {
|
||||
mkdirp(dirPath, function(err) {
|
||||
if(err) {
|
||||
reject(err);
|
||||
}
|
||||
else {
|
||||
resolve(dirPath);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
module.exports.pathSync = function(subPath) {
|
||||
var dirPath = genPath(subPath);
|
||||
mkdirp.sync(dirPath);
|
||||
return dirPath;
|
||||
}
|
||||
|
12
test/example-style.json
Normal file
12
test/example-style.json
Normal file
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"id": "test-style",
|
||||
"version": 8,
|
||||
"name": "Test Style",
|
||||
"metadata": {
|
||||
"maputnik:renderer": "mbgljs"
|
||||
},
|
||||
"sources": {},
|
||||
"glyphs": "https://demo.tileserver.org/fonts/{fontstack}/{range}.pbf",
|
||||
"sprites": "https://demo.tileserver.org/fonts/{fontstack}/{range}.pbf",
|
||||
"layers": []
|
||||
}
|
1077
test/functional/advanced.js
Normal file
1077
test/functional/advanced.js
Normal file
File diff suppressed because it is too large
Load diff
93
test/geojson-server.js
Normal file
93
test/geojson-server.js
Normal file
|
@ -0,0 +1,93 @@
|
|||
const cors = require("cors");
|
||||
const express = require("express");
|
||||
const fs = require("fs");
|
||||
const sourceData = require("./sources");
|
||||
|
||||
|
||||
var app = express();
|
||||
|
||||
app.use(cors());
|
||||
|
||||
|
||||
function buildStyle(opts) {
|
||||
opts = opts || {};
|
||||
opts = Object.assign({
|
||||
sources: {}
|
||||
}, opts);
|
||||
|
||||
return {
|
||||
"id": "test-style",
|
||||
"version": 8,
|
||||
"name": "Test Style",
|
||||
"metadata": {
|
||||
"maputnik:renderer": "mbgljs"
|
||||
},
|
||||
"sources": opts.sources,
|
||||
"glyphs": "https://demo.tileserver.org/fonts/{fontstack}/{range}.pbf",
|
||||
"sprites": "https://demo.tileserver.org/fonts/{fontstack}/{range}.pbf",
|
||||
"layers": []
|
||||
}
|
||||
}
|
||||
|
||||
function buildGeoJSONSource(data) {
|
||||
return {
|
||||
type: "vector",
|
||||
data: data
|
||||
};
|
||||
}
|
||||
|
||||
function buildResterSource(req, key) {
|
||||
return {
|
||||
"tileSize": 256,
|
||||
"tiles": [
|
||||
req.protocol + '://' + req.get('host') + "/" + key + "/{x}/{y}/{z}"
|
||||
],
|
||||
"type": "raster"
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
app.get("/sources/raster/{x}/{y}/{z}", function(req, res) {
|
||||
res.status(404).end();
|
||||
})
|
||||
|
||||
app.get("/styles/empty/:sources", function(req, res) {
|
||||
var reqSources = req.params.sources.split(",");
|
||||
|
||||
var sources = {};
|
||||
// reqSources.forEach(function(key) {
|
||||
// sources[key] = buildGeoJSONSource(sourceData[key]);
|
||||
// });
|
||||
reqSources.forEach(function(key) {
|
||||
var parts = key.split(":");
|
||||
var type = parts[0];
|
||||
var key = parts[1];
|
||||
|
||||
if(type === "geojson") {
|
||||
sources[key] = buildGeoJSONSource(sourceData[key]);
|
||||
}
|
||||
else if(type === "raster") {
|
||||
sources[key] = buildResterSource(req, key);
|
||||
}
|
||||
else {
|
||||
console.error("ERR: Invalid type: %s", type);
|
||||
throw "Invalid type"
|
||||
}
|
||||
});
|
||||
|
||||
var json = buildStyle({
|
||||
sources: sources
|
||||
});
|
||||
res.send(json);
|
||||
})
|
||||
|
||||
app.get("/example-style.json", function(req, res) {
|
||||
res.json(
|
||||
JSON.parse(
|
||||
fs.readFileSync(__dirname+"/example-style.json").toString()
|
||||
)
|
||||
);
|
||||
})
|
||||
|
||||
|
||||
module.exports = app;
|
15
test/sources/example.json
Normal file
15
test/sources/example.json
Normal file
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"type":"FeatureCollection",
|
||||
"features":[
|
||||
{
|
||||
"type":"Feature",
|
||||
"properties": {
|
||||
"name": "Dinagat Islands"
|
||||
},
|
||||
"geometry":{
|
||||
"type": "Point",
|
||||
"coordinates": [125.6, 10.1]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
3
test/sources/index.js
Normal file
3
test/sources/index.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
module.exports = {
|
||||
example: require("./example")
|
||||
};
|
|
@ -1,15 +0,0 @@
|
|||
var assert = require('assert');
|
||||
var config = require("../config/specs");
|
||||
|
||||
|
||||
describe('maputnik', function() {
|
||||
|
||||
it('check logo exists', function () {
|
||||
browser.url(config.baseUrl);
|
||||
browser.waitForExist(".maputnik-toolbar-link");
|
||||
|
||||
var src = browser.getAttribute(".maputnik-toolbar-link img", "src");
|
||||
assert.equal(src, config.baseUrl+'/img/logo-color.svg');
|
||||
});
|
||||
|
||||
});
|
6
test/wd-helper.js
Normal file
6
test/wd-helper.js
Normal file
|
@ -0,0 +1,6 @@
|
|||
module.exports = {
|
||||
"$": function(key, selector) {
|
||||
selector = selector || "";
|
||||
return "*[data-wd-key='"+key+"'] "+selector;
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue