diff --git a/.circleci/config.yml b/.circleci/config.yml index 63fa403c..27bf9933 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -52,18 +52,18 @@ jobs: build-linux-node-v8: docker: - image: node:8 - - image: selenium/standalone-chrome:3.8.1 working_directory: ~/repo-linux-node-v8 - steps: *wdio-steps + steps: *build-steps build-linux-node-v10: docker: - image: node:10 + - image: selenium/standalone-chrome:3.141.59 working_directory: ~/repo-linux-node-v10 - steps: *build-steps - build-linux-node-v11: + steps: *wdio-steps + build-linux-node-v12: docker: - - image: node:11 - working_directory: ~/repo-linux-node-v11 + - image: node:12 + working_directory: ~/repo-linux-node-v12 steps: *build-steps build-osx-node-v8: macos: @@ -81,13 +81,13 @@ jobs: - brew install node@10 working_directory: ~/repo-osx-node-v10 steps: *build-steps - build-osx-node-v11: + build-osx-node-v12: macos: xcode: "9.0" dependencies: override: - - brew install node@11 - working_directory: ~/repo-osx-node-v11 + - brew install node@12 + working_directory: ~/repo-osx-node-v12 steps: *build-steps workflows: @@ -96,7 +96,7 @@ workflows: jobs: - build-linux-node-v8 - build-linux-node-v10 - - build-linux-node-v11 + - build-linux-node-v12 - build-osx-node-v8 - build-osx-node-v10 - - build-osx-node-v11 + - build-osx-node-v12 diff --git a/appveyor.yml b/appveyor.yml index d2e36837..c5a86e52 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -3,12 +3,19 @@ environment: matrix: - nodejs_version: "8" - nodejs_version: "10" - - nodejs_version: "11" + - nodejs_version: "12" platform: - x86 - x64 install: - - ps: Install-Product node $env:nodejs_version + # https://github.com/appveyor/ci/issues/2921#issuecomment-501016533 + - ps: | + try { + Install-Product node $env:nodejs_version $env:platform + } catch { + echo "Unable to install node $env:nodejs_version, trying update..." + Update-NodeJsInstallation (Get-NodeJsLatestBuild $env:nodejs_version) $env:platform + } - md public - npm --vs2015 install --global windows-build-tools - npm install diff --git a/config/wdio.conf.js b/config/wdio.conf.js index b520bcfc..91b4c6ef 100644 --- a/config/wdio.conf.js +++ b/config/wdio.conf.js @@ -10,53 +10,279 @@ var server; var SCREENSHOT_PATH = artifacts.pathSync("screenshots"); exports.config = { - specs: [ - './test/functional/index.js' - ], - exclude: [ - ], - maxInstances: 10, - capabilities: [{ - maxInstances: 5, - browserName: 'chrome' - }], - sync: true, - logLevel: 'verbose', - coloredLogs: true, - bail: 0, - screenshotPath: SCREENSHOT_PATH, - // Note: This is here because @orangemug currently runs Maputnik inside a docker container. - host: process.env.DOCKER_HOST || "0.0.0.0", - baseUrl: 'http://localhost', - waitforTimeout: 10000, - connectionRetryTimeout: 90000, - connectionRetryCount: 3, - framework: 'mocha', - reporters: ['spec'], - mochaOpts: { - ui: 'bdd', - // Because we don't know how long the initial build will take... - timeout: 4*60*1000 - }, - onPrepare: function (config, capabilities) { - return new Promise(function(resolve, reject) { - var compiler = webpack(webpackConfig); - server = new WebpackDevServer(compiler, { - stats: { - colors: true - } - }); - server.listen(testConfig.port, (isDocker() ? "0.0.0.0" : "localhost"), function(err) { - if(err) { - reject(err); - } - else { - resolve(); - } - }); - }) - }, - onComplete: function(exitCode) { - server.close() - } + // + // ==================== + // Runner Configuration + // ==================== + // + // WebdriverIO allows it to run your tests in arbitrary locations (e.g. locally or + // on a remote machine). + runner: 'local', + // + // ================== + // Specify Test Files + // ================== + // Define which test specs should run. The pattern is relative to the directory + // from which `wdio` was called. Notice that, if you are calling `wdio` from an + // NPM script (see https://docs.npmjs.com/cli/run-script) then the current working + // directory is where your package.json resides, so `wdio` will be called from there. + // + specs: [ + './test/functional/index.js' + ], + // Patterns to exclude. + exclude: [ + // 'path/to/excluded/files' + ], + // + // ============ + // Capabilities + // ============ + // Define your capabilities here. WebdriverIO can run multiple capabilities at the same + // time. Depending on the number of capabilities, WebdriverIO launches several test + // sessions. Within your capabilities you can overwrite the spec and exclude options in + // order to group specific specs to a specific capability. + // + // First, you can define how many instances should be started at the same time. Let's + // say you have 3 different capabilities (Chrome, Firefox, and Safari) and you have + // set maxInstances to 1; wdio will spawn 3 processes. Therefore, if you have 10 spec + // files and you set maxInstances to 10, all spec files will get tested at the same time + // and 30 processes will get spawned. The property handles how many capabilities + // from the same test should run tests. + // + maxInstances: 10, + // + // If you have trouble getting all important capabilities together, check out the + // Sauce Labs platform configurator - a great tool to configure your capabilities: + // https://docs.saucelabs.com/reference/platforms-configurator + // + capabilities: [{ + // maxInstances can get overwritten per capability. So if you have an in-house Selenium + // grid with only 5 firefox instances available you can make sure that not more than + // 5 instances get started at a time. + maxInstances: 5, + // + browserName: 'chrome', + // If outputDir is provided WebdriverIO can capture driver session logs + // it is possible to configure which logTypes to include/exclude. + // excludeDriverLogs: ['*'], // pass '*' to exclude all driver session logs + // excludeDriverLogs: ['bugreport', 'server'], + }], + // + // =================== + // Test Configurations + // =================== + // Define all options that are relevant for the WebdriverIO instance here + // + // Level of logging verbosity: trace | debug | info | warn | error | silent + logLevel: 'info', + // + // Set specific log levels per logger + // loggers: + // - webdriver, webdriverio + // - @wdio/applitools-service, @wdio/browserstack-service, @wdio/devtools-service, @wdio/sauce-service + // - @wdio/mocha-framework, @wdio/jasmine-framework + // - @wdio/local-runner, @wdio/lambda-runner + // - @wdio/sumologic-reporter + // - @wdio/cli, @wdio/config, @wdio/sync, @wdio/utils + // Level of logging verbosity: trace | debug | info | warn | error | silent + // logLevels: { + // webdriver: 'debug', + // '@wdio/applitools-service': 'info' + // }, + // + // If you only want to run your tests until a specific amount of tests have failed use + // bail (default is 0 - don't bail, run all tests). + bail: 0, + // + screenshotPath: SCREENSHOT_PATH, + // Note: This is here because @orangemug currently runs Maputnik inside a docker container. + host: process.env.DOCKER_HOST || "0.0.0.0", + // Set a base URL in order to shorten url command calls. If your `url` parameter starts + // with `/`, the base url gets prepended, not including the path portion of your baseUrl. + // If your `url` parameter starts without a scheme or `/` (like `some/path`), the base url + // gets prepended directly. + baseUrl: 'http://localhost', + // + // Default timeout for all waitFor* commands. + waitforTimeout: 10000, + // + // Default timeout in milliseconds for request + // if Selenium Grid doesn't send response + connectionRetryTimeout: 90000, + // + // Default request retries count + connectionRetryCount: 3, + // + // Test runner services + // Services take over a specific job you don't want to take care of. They enhance + // your test setup with almost no effort. Unlike plugins, they don't add new + // commands. Instead, they hook themselves up into the test process. + services: ['selenium-standalone'], + // + // Framework you want to run your specs with. + // The following are supported: Mocha, Jasmine, and Cucumber + // see also: https://webdriver.io/docs/frameworks.html + // + // Make sure you have the wdio adapter package for the specific framework installed + // before running any tests. + framework: 'mocha', + // + // The number of times to retry the entire specfile when it fails as a whole + // specFileRetries: 1, + // + // Test reporter for stdout. + // The only one supported by default is 'dot' + // see also: https://webdriver.io/docs/dot-reporter.html + reporters: ['spec'], + + // + // Options to be passed to Mocha. + // See the full list at http://mochajs.org/ + mochaOpts: { + ui: 'bdd', + // Because we don't know how long the initial build will take... + timeout: 4*60*1000 + }, + onPrepare: function (config, capabilities) { + return new Promise(function(resolve, reject) { + var compiler = webpack(webpackConfig); + server = new WebpackDevServer(compiler, { + stats: { + colors: true + } + }); + server.listen(testConfig.port, (isDocker() ? "0.0.0.0" : "localhost"), function(err) { + if(err) { + reject(err); + } + else { + resolve(); + } + }); + }) + }, + onComplete: function(exitCode) { + server.close() + } + // + // ===== + // Hooks + // ===== + // WebdriverIO provides several hooks you can use to interfere with the test process in order to enhance + // it and to build services around it. You can either apply a single function or an array of + // methods to it. If one of them returns with a promise, WebdriverIO will wait until that promise got + // resolved to continue. + /** + * Gets executed once before all workers get launched. + * @param {Object} config wdio configuration object + * @param {Array.} capabilities list of capabilities details + */ + // onPrepare: function (config, capabilities) { + // }, + /** + * Gets executed just before initialising the webdriver session and test framework. It allows you + * to manipulate configurations depending on the capability or spec. + * @param {Object} config wdio configuration object + * @param {Array.} capabilities list of capabilities details + * @param {Array.} specs List of spec file paths that are to be run + */ + // beforeSession: function (config, capabilities, specs) { + // }, + /** + * Gets executed before test execution begins. At this point you can access to all global + * variables like `browser`. It is the perfect place to define custom commands. + * @param {Array.} capabilities list of capabilities details + * @param {Array.} specs List of spec file paths that are to be run + */ + // before: function (capabilities, specs) { + // }, + /** + * Runs before a WebdriverIO command gets executed. + * @param {String} commandName hook command name + * @param {Array} args arguments that command would receive + */ + // beforeCommand: function (commandName, args) { + // }, + + /** + * Hook that gets executed before the suite starts + * @param {Object} suite suite details + */ + // beforeSuite: function (suite) { + // }, + /** + * Function to be executed before a test (in Mocha/Jasmine) or a step (in Cucumber) starts. + * @param {Object} test test details + */ + // beforeTest: function (test) { + // }, + /** + * Hook that gets executed _before_ a hook within the suite starts (e.g. runs before calling + * beforeEach in Mocha) + */ + // beforeHook: function () { + // }, + /** + * Hook that gets executed _after_ a hook within the suite starts (e.g. runs after calling + * afterEach in Mocha) + */ + // afterHook: function () { + // }, + /** + * Function to be executed after a test (in Mocha/Jasmine) or a step (in Cucumber) starts. + * @param {Object} test test details + */ + // afterTest: function (test) { + // }, + /** + * Hook that gets executed after the suite has ended + * @param {Object} suite suite details + */ + // afterSuite: function (suite) { + // }, + + /** + * Runs after a WebdriverIO command gets executed + * @param {String} commandName hook command name + * @param {Array} args arguments that command would receive + * @param {Number} result 0 - command success, 1 - command error + * @param {Object} error error object if any + */ + // afterCommand: function (commandName, args, result, error) { + // }, + /** + * Gets executed after all tests are done. You still have access to all global variables from + * the test. + * @param {Number} result 0 - test pass, 1 - test fail + * @param {Array.} capabilities list of capabilities details + * @param {Array.} specs List of spec file paths that ran + */ + // after: function (result, capabilities, specs) { + // }, + /** + * Gets executed right after terminating the webdriver session. + * @param {Object} config wdio configuration object + * @param {Array.} capabilities list of capabilities details + * @param {Array.} specs List of spec file paths that ran + */ + // afterSession: function (config, capabilities, specs) { + // }, + /** + * Gets executed after all workers got shut down and the process is about to exit. An error + * thrown in the onComplete hook will result in the test run failing. + * @param {Object} exitCode 0 - success, 1 - fail + * @param {Object} config wdio configuration object + * @param {Array.} capabilities list of capabilities details + * @param {} results object containing test results + */ + // onComplete: function(exitCode, config, capabilities, results) { + // }, + /** + * Gets executed when a refresh happens. + * @param {String} oldSessionId session ID of the old session + * @param {String} newSessionId session ID of the new session + */ + //onReload: function(oldSessionId, newSessionId) { + //} } diff --git a/package.json b/package.json index 37ad4d30..4c55e58a 100644 --- a/package.json +++ b/package.json @@ -26,18 +26,20 @@ "classnames": "^2.2.6", "codemirror": "^5.40.2", "color": "^3.0.0", + "detect-browser": "^4.5.0", "file-saver": "^1.3.8", "jsonlint": "github:josdejong/jsonlint#85a19d7", "lodash.capitalize": "^4.2.1", "lodash.clamp": "^4.0.3", "lodash.clonedeep": "^4.5.0", + "lodash.get": "^4.4.2", "lodash.isequal": "^4.5.0", "lodash.throttle": "^4.1.1", "mapbox-gl": "^1.1.0-beta.1", "mapbox-gl-inspect": "^1.3.1", "maputnik-design": "github:maputnik/design", - "ol": "^5.2.0", - "ol-mapbox-style": "^3.1.0", + "ol": "^6.0.0-beta.8", + "ol-mapbox-style": "^5.0.0-beta.2", "prop-types": "^15.6.2", "react": "^16.5.2", "react-aria-menubutton": "^6.0.1", @@ -104,6 +106,12 @@ "@babel/preset-env": "^7.1.0", "@babel/preset-flow": "^7.0.0", "@babel/preset-react": "^7.0.0", + "@wdio/cli": "^5.10.4", + "@wdio/local-runner": "^5.10.4", + "@wdio/mocha-framework": "^5.10.1", + "@wdio/selenium-standalone-service": "^5.9.3", + "@wdio/spec-reporter": "^5.9.3", + "@wdio/sync": "^5.10.1", "babel-eslint": "^10.0.1", "babel-loader": "8.0.4", "babel-plugin-istanbul": "^5.0.1", @@ -113,29 +121,26 @@ "css-loader": "^1.0.0", "eslint": "^5.6.1", "eslint-plugin-react": "^7.11.1", - "express": "^4.16.3", + "express": "^4.17.1", "file-loader": "^2.0.0", "html-webpack-plugin": "^3.2.0", - "is-docker": "^1.1.0", + "is-docker": "^2.0.0", "istanbul": "^0.4.5", "istanbul-lib-coverage": "^2.0.1", "mkdirp": "^0.5.1", - "mocha": "^5.2.0", - "node-sass": "^4.10.0", + "mocha": "^6.1.4", + "node-sass": "^4.12.0", "raw-loader": "^0.5.1", "react-hot-loader": "^4.3.11", "sass-loader": "^7.1.0", - "selenium-standalone": "^6.15.3", + "selenium-standalone": "^6.16.0", "style-loader": "^0.23.0", "stylelint": "^10.0.0", "stylelint-config-recommended-scss": "^3.2.0", "stylelint-scss": "^3.5.4", "transform-loader": "^0.2.4", "uuid": "^3.3.2", - "wdio-mocha-framework": "^0.6.4", - "wdio-selenium-standalone-service": "0.0.10", - "wdio-spec-reporter": "^0.1.5", - "webdriverio": "^4.13.2", + "webdriverio": "^5.10.4", "webpack": "^4.20.2", "webpack-bundle-analyzer": "^3.0.2", "webpack-cleanup-plugin": "^0.5.1", diff --git a/src/components/App.jsx b/src/components/App.jsx index 34216c84..d9a4bce8 100644 --- a/src/components/App.jsx +++ b/src/components/App.jsx @@ -2,6 +2,7 @@ import autoBind from 'react-autobind'; import React from 'react' import cloneDeep from 'lodash.clonedeep' import clamp from 'lodash.clamp' +import get from 'lodash.get' import {arrayMove} from 'react-sortable-hoc' import url from 'url' @@ -19,6 +20,7 @@ import SourcesModal from './modals/SourcesModal' import OpenModal from './modals/OpenModal' import ShortcutsModal from './modals/ShortcutsModal' import SurveyModal from './modals/SurveyModal' +import DebugModal from './modals/DebugModal' import { downloadGlyphsMetadata, downloadSpriteMetadata } from '../libs/metadata' import {latest, validate} from '@mapbox/mapbox-gl-style-spec' @@ -139,6 +141,12 @@ export default class App extends React.Component { document.querySelector(".mapboxgl-canvas").focus(); } }, + { + key: "!", + handler: () => { + this.toggleModal("debug"); + } + }, ] document.body.addEventListener("keyup", (e) => { @@ -203,12 +211,16 @@ export default class App extends React.Component { open: false, shortcuts: false, export: false, - survey: localStorage.hasOwnProperty('survey') ? false : true + survey: localStorage.hasOwnProperty('survey') ? false : true, + debug: false, }, - mapOptions: { - showTileBoundaries: queryUtil.asBool(queryObj, "show-tile-boundaries"), - showCollisionBoxes: queryUtil.asBool(queryObj, "show-collision-boxes"), - showOverdrawInspector: queryUtil.asBool(queryObj, "show-overdraw-inspector") + mapboxGlDebugOptions: { + showTileBoundaries: false, + showCollisionBoxes: false, + showOverdrawInspector: false, + }, + openlayersDebugOptions: { + debugToolbox: false, }, } @@ -217,20 +229,24 @@ export default class App extends React.Component { }) } - handleKeyPress(e) { + handleKeyPress = (e) => { if(navigator.platform.toUpperCase().indexOf('MAC') >= 0) { if(e.metaKey && e.shiftKey && e.keyCode === 90) { + e.preventDefault(); this.onRedo(e); } else if(e.metaKey && e.keyCode === 90) { + e.preventDefault(); this.onUndo(e); } } else { if(e.ctrlKey && e.keyCode === 90) { + e.preventDefault(); this.onUndo(e); } else if(e.ctrlKey && e.keyCode === 89) { + e.preventDefault(); this.onRedo(e); } } @@ -264,6 +280,27 @@ export default class App extends React.Component { }) } + onChangeMetadataProperty = (property, value) => { + // If we're changing renderer reset the map state. + if ( + property === 'maputnik:renderer' && + value !== get(this.state.mapStyle, ['metadata', 'maputnik:renderer'], 'mbgljs') + ) { + this.setState({ + mapState: 'map' + }); + } + + const changedStyle = { + ...this.state.mapStyle, + metadata: { + ...this.state.mapStyle.metadata, + [property]: value + } + } + this.onStyleChanged(changedStyle) + } + onStyleChanged = (newStyle, save=true) => { const errors = validate(newStyle, latest) @@ -397,6 +434,27 @@ export default class App extends React.Component { }) } + setDefaultValues = (styleObj) => { + const metadata = styleObj.metadata || {} + if(metadata['maputnik:renderer'] === undefined) { + const changedStyle = { + ...styleObj, + metadata: { + ...styleObj.metadata, + 'maputnik:renderer': 'mbgljs' + } + } + return changedStyle + } else { + return styleObj + } + } + + openStyle = (styleObj) => { + styleObj = this.setDefaultValues(styleObj) + this.onStyleChanged(styleObj) + } + fetchSources() { const sourceList = {...this.state.sources}; @@ -461,18 +519,23 @@ export default class App extends React.Component { } } + _getRenderer () { + const metadata = this.state.mapStyle.metadata || {}; + return metadata['maputnik:renderer'] || 'mbgljs'; + } + mapRenderer() { + const metadata = this.state.mapStyle.metadata || {}; + const mapProps = { mapStyle: style.replaceAccessTokens(this.state.mapStyle, {allowFallback: true}), - options: this.state.mapOptions, onDataChange: (e) => { this.layerWatcher.analyzeMap(e.map) this.fetchSources(); }, } - const metadata = this.state.mapStyle.metadata || {} - const renderer = metadata['maputnik:renderer'] || 'mbgljs' + const renderer = this._getRenderer(); let mapElement; @@ -480,9 +543,12 @@ export default class App extends React.Component { if(renderer === 'ol') { mapElement = } else { mapElement = @@ -524,12 +590,31 @@ export default class App extends React.Component { this.setModal(modalName, !this.state.isOpen[modalName]); } + onChangeOpenlayersDebug = (key, value) => { + this.setState({ + openlayersDebugOptions: { + ...this.state.openlayersDebugOptions, + [key]: value, + } + }); + } + + onChangeMaboxGlDebug = (key, value) => { + this.setState({ + mapboxGlDebugOptions: { + ...this.state.mapboxGlDebugOptions, + [key]: value, + } + }); + } + render() { const layers = this.state.mapStyle.layers || [] const selectedLayer = layers.length > 0 ? layers[this.state.selectedLayerIndex] : null const metadata = this.state.mapStyle.metadata || {} const toolbar = + this.shortcutEl = el} isOpen={this.state.isOpen.shortcuts} @@ -583,8 +677,10 @@ export default class App extends React.Component { , there isn't another way to detect support that I'm aware of. +const browser = detect(); +const colorAccessibilityFiltersEnabled = ['chrome', 'firefox'].indexOf(browser.name) > -1; + + class IconText extends React.Component { static propTypes = { children: PropTypes.node, @@ -108,6 +114,7 @@ export default class Toolbar extends React.Component { onToggleModal: PropTypes.func, onSetMapState: PropTypes.func, mapState: PropTypes.string, + renderer: PropTypes.string, } state = { @@ -133,22 +140,27 @@ export default class Toolbar extends React.Component { { id: "inspect", title: "Inspect", + disabled: this.props.renderer !== 'mbgljs', }, { id: "filter-deuteranopia", title: "Map (deuteranopia)", + disabled: !colorAccessibilityFiltersEnabled, }, { id: "filter-protanopia", title: "Map (protanopia)", + disabled: !colorAccessibilityFiltersEnabled, }, { id: "filter-tritanopia", title: "Map (tritanopia)", + disabled: !colorAccessibilityFiltersEnabled, }, { id: "filter-achromatopsia", title: "Map (achromatopsia)", + disabled: !colorAccessibilityFiltersEnabled, }, ]; @@ -201,7 +213,7 @@ export default class Toolbar extends React.Component { this.props.onChangeMaboxGlDebug(key, e.target.checked)} /> {key} + + + })} + + } + {this.props.renderer === 'ol' && +
    + {Object.entries(this.props.openlayersDebugOptions).map(([key, val]) => { + return
  • + +
  • + })} +
+ } + + + } +} + +export default DebugModal; diff --git a/src/components/modals/SettingsModal.jsx b/src/components/modals/SettingsModal.jsx index 6d38b2dc..04ca51d1 100644 --- a/src/components/modals/SettingsModal.jsx +++ b/src/components/modals/SettingsModal.jsx @@ -11,6 +11,7 @@ class SettingsModal extends React.Component { static propTypes = { mapStyle: PropTypes.object.isRequired, onStyleChanged: PropTypes.func.isRequired, + onChangeMetadataProperty: PropTypes.func.isRequired, isOpen: PropTypes.bool.isRequired, onOpenToggle: PropTypes.func.isRequired, } @@ -23,19 +24,9 @@ class SettingsModal extends React.Component { this.props.onStyleChanged(changedStyle) } - changeMetadataProperty(property, value) { - const changedStyle = { - ...this.props.mapStyle, - metadata: { - ...this.props.mapStyle.metadata, - [property]: value - } - } - this.props.onStyleChanged(changedStyle) - } - render() { const metadata = this.props.mapStyle.metadata || {} + const {onChangeMetadataProperty} = this.props; const inputProps = { } return @@ -86,7 +77,7 @@ class SettingsModal extends React.Component { @@ -94,7 +85,7 @@ class SettingsModal extends React.Component { @@ -106,9 +97,10 @@ class SettingsModal extends React.Component { ['ol', 'Open Layers (experimental)'], ]} value={metadata['maputnik:renderer'] || 'mbgljs'} - onChange={this.changeMetadataProperty.bind(this, 'maputnik:renderer')} + onChange={onChangeMetadataProperty.bind(this, 'maputnik:renderer')} /> + } diff --git a/src/components/modals/ShortcutsModal.jsx b/src/components/modals/ShortcutsModal.jsx index df8312ee..1ec7448b 100644 --- a/src/components/modals/ShortcutsModal.jsx +++ b/src/components/modals/ShortcutsModal.jsx @@ -40,6 +40,10 @@ class ShortcutsModal extends React.Component { key: "m", text: "Focus map" }, + { + key: "!", + text: "Debug modal" + }, ] diff --git a/src/styles/_map.scss b/src/styles/_map.scss index 951c654a..c45eab3a 100644 --- a/src/styles/_map.scss +++ b/src/styles/_map.scss @@ -1,7 +1,13 @@ //OPENLAYERS .maputnik-layout { .ol-zoom { - top: 10px; + top: 40px; + right: 10px; + left: auto; + } + + .ol-rotate { + top: 94px; right: 10px; left: auto; } @@ -20,3 +26,57 @@ } } } + + +.maputnik-ol { + width: 100%; + height: 100%; +} + +.maputnik-ol-popup { + background: $color-black; + +} + +.maputnik-coords { + font-family: monospace; + &:before { + content: '['; + color: #888; + } + &:after { + content: ']'; + color: #888; + } +} + +.maputnik-ol-debug { + font-family: monospace; + font-size: smaller; + position: absolute; + bottom: 10px; + left: 10px; + background: rgb(28, 31, 36); + padding: 6px 8px; + border-radius: 2px; + z-index: 9999; +} + +.maputnik-ol-zoom { + position: absolute; + right: 10px; + top: 10px; + background: #1c1f24; + border-radius: 2px; + padding: 6px 8px; + color: $color-lowgray; + z-index: 9999; + font-size: 12px; + font-weight: bold; +} + +.maputnik-ol-container { + display: flex; + flex: 1; + position: relative; +} diff --git a/src/styles/_popup.scss b/src/styles/_popup.scss index 1c6ad926..2338f899 100644 --- a/src/styles/_popup.scss +++ b/src/styles/_popup.scss @@ -1,4 +1,15 @@ .maputnik-popup-layer { + display: flex; + flex-direction: row; +} + +.maputnik-popup-layer__swatch { + display: inline-block; + width: 5px; + align-content: stretch; +} + +.maputnik-popup-layer__label { display: block; color: $color-lowgray; cursor: pointer; @@ -11,7 +22,7 @@ .maputnik-popup-layer-id { padding-left: $margin-2; - padding-right: $margin-2; + padding-right: 1.6em; background-color: $color-midgray; color: $color-white; } diff --git a/test/functional/helper.js b/test/functional/helper.js index 98324502..bca53ccb 100644 --- a/test/functional/helper.js +++ b/test/functional/helper.js @@ -18,7 +18,7 @@ module.exports = { var result = browser.executeAsync(function(done) { window.debug.get("maputnik", "styleStore").latestStyle(done); }) - return result.value; + return result; }, getRevisionStore: function(browser) { var result = browser.execute(function(done) { @@ -34,15 +34,16 @@ module.exports = { modal: { addLayer: { open: function() { - var selector = wd.$('layer-list:add-layer'); - browser.click(selector); + const selector = $(wd.$('layer-list:add-layer')); + selector.click(); // Wait for events browser.flushReactUpdates(); - browser.waitForExist(wd.$('modal:add-layer')); - browser.isVisible(wd.$('modal:add-layer')); - browser.isVisibleWithinViewport(wd.$('modal:add-layer')); + const elem = $(wd.$('modal:add-layer')); + elem.waitForExist(); + elem.isDisplayed(); + elem.isDisplayedInViewport(); // Wait for events browser.flushReactUpdates(); @@ -58,7 +59,8 @@ module.exports = { id = type+":"+uuid(); } - browser.selectByValue(wd.$("add-layer.layer-type", "select"), type); + const selectBox = $(wd.$("add-layer.layer-type", "select")); + selectBox.selectByAttribute('value', type); browser.flushReactUpdates(); browser.setValueSafe(wd.$("add-layer.layer-id", "input"), id); @@ -67,7 +69,8 @@ module.exports = { } browser.flushReactUpdates(); - browser.click(wd.$("add-layer")); + const elem_addLayer = $(wd.$("add-layer")); + elem_addLayer.click(); return id; } diff --git a/test/functional/index.js b/test/functional/index.js index 53e61958..2bb11646 100644 --- a/test/functional/index.js +++ b/test/functional/index.js @@ -11,11 +11,12 @@ describe('maputnik', function() { "geojson:example", "raster:raster" ])); - browser.alertAccept(); + browser.acceptAlert(); browser.execute(function() { localStorage.setItem("survey", true); }); - browser.waitForExist(".maputnik-toolbar-link"); + const elem = $(".maputnik-toolbar-link"); + elem.waitForExist(); browser.flushReactUpdates(); }); diff --git a/test/functional/layers/index.js b/test/functional/layers/index.js index 048a7951..7665bc1c 100644 --- a/test/functional/layers/index.js +++ b/test/functional/layers/index.js @@ -11,8 +11,9 @@ describe("layers", function() { "geojson:example", "raster:raster" ])); - browser.alertAccept(); - browser.waitForExist(".maputnik-toolbar-link"); + browser.acceptAlert(); + const elem = $(".maputnik-toolbar-link"); + elem.waitForExist(); browser.flushReactUpdates(); helper.modal.addLayer.open(); @@ -33,7 +34,8 @@ describe("layers", function() { }, ]); - browser.click(wd.$("layer-list-item:"+id+":delete", "")); + const elem = $(wd.$("layer-list-item:"+id+":delete", "")); + elem.click(); styleObj = helper.getStyleStore(browser); assert.deepEqual(styleObj.layers, [ @@ -54,7 +56,8 @@ describe("layers", function() { }, ]); - browser.click(wd.$("layer-list-item:"+id+":copy", "")); + const elem = $(wd.$("layer-list-item:"+id+":copy", "")); + elem.click(); styleObj = helper.getStyleStore(browser); assert.deepEqual(styleObj.layers, [ @@ -83,7 +86,8 @@ describe("layers", function() { }, ]); - browser.click(wd.$("layer-list-item:"+id+":toggle-visibility", "")); + const elem = $(wd.$("layer-list-item:"+id+":toggle-visibility", "")); + elem.click(); styleObj = helper.getStyleStore(browser); assert.deepEqual(styleObj.layers, [ @@ -96,7 +100,7 @@ describe("layers", function() { }, ]); - browser.click(wd.$("layer-list-item:"+id+":toggle-visibility", "")); + elem.click(); styleObj = helper.getStyleStore(browser); assert.deepEqual(styleObj.layers, [ @@ -147,11 +151,13 @@ describe("layers", function() { // Setup var id = uuid(); - browser.selectByValue(wd.$("add-layer.layer-type", "select"), "background"); + const selectBox = $(wd.$("add-layer.layer-type", "select")); + selectBox.selectByAttribute('value', "background"); browser.flushReactUpdates(); browser.setValueSafe(wd.$("add-layer.layer-id", "input"), "background:"+id); - browser.click(wd.$("add-layer")); + const elem = $(wd.$("add-layer")); + elem.click(); var styleObj = helper.getStyleStore(browser); assert.deepEqual(styleObj.layers, [ @@ -169,11 +175,13 @@ describe("layers", function() { it("id", function() { var bgId = createBackground(); - browser.click(wd.$("layer-list-item:background:"+bgId)) + const elem = $(wd.$("layer-list-item:background:"+bgId)); + elem.click(); var id = uuid(); browser.setValueSafe(wd.$("layer-editor.layer-id", "input"), "foobar:"+id) - browser.click(wd.$("min-zoom")) + const elem2 = $(wd.$("min-zoom")); + elem2.click(); var styleObj = helper.getStyleStore(browser); assert.deepEqual(styleObj.layers, [ @@ -190,9 +198,11 @@ describe("layers", function() { it("min-zoom", function() { var bgId = createBackground(); - browser.click(wd.$("layer-list-item:background:"+bgId)) + const elem = $(wd.$("layer-list-item:background:"+bgId)); + elem.click(); browser.setValueSafe(wd.$("min-zoom", "input"), 1) - browser.click(wd.$("layer-editor.layer-id", "input")); + const elem2 = $(wd.$("layer-editor.layer-id", "input")); + elem2.click(); var styleObj = helper.getStyleStore(browser); assert.deepEqual(styleObj.layers, [ @@ -220,9 +230,11 @@ describe("layers", function() { it("max-zoom", function() { var bgId = createBackground(); - browser.click(wd.$("layer-list-item:background:"+bgId)) + const elem = $(wd.$("layer-list-item:background:"+bgId)); + elem.click(); browser.setValueSafe(wd.$("max-zoom", "input"), 1) - browser.click(wd.$("layer-editor.layer-id", "input")); + const elem2 = $(wd.$("layer-editor.layer-id", "input")); + elem2.click(); var styleObj = helper.getStyleStore(browser); assert.deepEqual(styleObj.layers, [ @@ -238,9 +250,11 @@ describe("layers", function() { var bgId = createBackground(); var id = uuid(); - browser.click(wd.$("layer-list-item:background:"+bgId)); + const elem = $(wd.$("layer-list-item:background:"+bgId)); + elem.click(); browser.setValueSafe(wd.$("layer-comment", "textarea"), id); - browser.click(wd.$("layer-editor.layer-id", "input")); + const elem2 = $(wd.$("layer-editor.layer-id", "input")); + elem2.click(); var styleObj = helper.getStyleStore(browser); assert.deepEqual(styleObj.layers, [ @@ -484,4 +498,3 @@ describe("layers", function() { }) }) }); - diff --git a/test/functional/modals/index.js b/test/functional/modals/index.js index 3726e97b..bb9477b9 100644 --- a/test/functional/modals/index.js +++ b/test/functional/modals/index.js @@ -7,14 +7,16 @@ var helper = require("../helper"); function closeModal(wdKey) { browser.waitUntil(function() { - return browser.isVisibleWithinViewport(wd.$(wdKey)); + const elem = $(wdKey); + return elem.isDisplayedInViewport(); }); - var closeBtnSelector = wd.$(wdKey+".close-modal"); - browser.click(closeBtnSelector); + const closeBtnSelector = $(wd.$(wdKey+".close-modal")); + closeBtnSelector.click(); browser.waitUntil(function() { - return !browser.isVisibleWithinViewport(wd.$(wdKey)); + const elem = $(wdKey); + return !elem.isDisplayed(); }); } @@ -26,10 +28,12 @@ describe("modals", function() { beforeEach(function() { browser.url(config.baseUrl+"?debug"); - browser.waitForExist(".maputnik-toolbar-link"); + const elem = $(".maputnik-toolbar-link"); + elem.waitForExist(); browser.flushReactUpdates(); - browser.click(wd.$("nav:open")) + const elem2 = $(wd.$("nav:open")); + elem2.click(); browser.flushReactUpdates(); }); @@ -37,8 +41,10 @@ describe("modals", function() { closeModal("open-modal"); }); - it("upload", function() { - browser.waitForExist("*[type='file']") + // "chooseFile" command currently not available for wdio v5 https://github.com/webdriverio/webdriverio/pull/3632 + it.skip("upload", function() { + const elem = $("*[type='file']"); + elem.waitForExist(); browser.chooseFile("*[type='file']", styleFilePath); var styleObj = helper.getStyleStore(browser); @@ -50,8 +56,8 @@ describe("modals", function() { browser.setValueSafe(wd.$("open-modal.url.input"), styleFileUrl); - var selector = wd.$("open-modal.url.button"); - browser.click(selector); + const selector = $(wd.$("open-modal.url.button")); + selector.click(); // Allow the network request to happen // NOTE: Its localhost so this should be fast. @@ -70,10 +76,12 @@ describe("modals", function() { beforeEach(function() { browser.url(config.baseUrl+"?debug"); - browser.waitForExist(".maputnik-toolbar-link"); + const elem = $(".maputnik-toolbar-link"); + elem.waitForExist(); browser.flushReactUpdates(); - browser.click(wd.$("nav:export")) + const elem2 = $(wd.$("nav:export")); + elem2.click(); browser.flushReactUpdates(); }); @@ -99,9 +107,10 @@ describe("modals", function() { browser.url(config.baseUrl+"?debug&style="+helper.getStyleUrl([ "geojson:example" ])); - browser.alertAccept(); + browser.acceptAlert(); - browser.selectByValue(wd.$("nav:inspect", "select"), "inspect"); + const selectBox = $(wd.$("nav:inspect", "select")); + selectBox.selectByAttribute('value', "inspect"); }) }) @@ -109,16 +118,19 @@ describe("modals", function() { beforeEach(function() { browser.url(config.baseUrl+"?debug"); - browser.waitForExist(".maputnik-toolbar-link"); + const elem = $(".maputnik-toolbar-link"); + elem.waitForExist(); browser.flushReactUpdates(); - browser.click(wd.$("nav:settings")) + const elem2 = $(wd.$("nav:settings")); + elem2.click(); browser.flushReactUpdates(); }); it("name", function() { browser.setValueSafe(wd.$("modal-settings.name"), "foobar") - browser.click(wd.$("modal-settings.owner")) + const elem = $(wd.$("modal-settings.owner")); + elem.click(); browser.flushReactUpdates(); var styleObj = helper.getStyleStore(browser); @@ -126,7 +138,8 @@ describe("modals", function() { }) it("owner", function() { browser.setValueSafe(wd.$("modal-settings.owner"), "foobar") - browser.click(wd.$("modal-settings.name")) + const elem = $(wd.$("modal-settings.name")); + elem.click(); browser.flushReactUpdates(); var styleObj = helper.getStyleStore(browser); @@ -134,7 +147,8 @@ describe("modals", function() { }) it("sprite url", function() { browser.setValueSafe(wd.$("modal-settings.sprite"), "http://example.com") - browser.click(wd.$("modal-settings.name")) + const elem = $(wd.$("modal-settings.name")); + elem.click(); browser.flushReactUpdates(); var styleObj = helper.getStyleStore(browser); @@ -143,7 +157,8 @@ describe("modals", function() { it("glyphs url", function() { var glyphsUrl = "http://example.com/{fontstack}/{range}.pbf" browser.setValueSafe(wd.$("modal-settings.glyphs"), glyphsUrl) - browser.click(wd.$("modal-settings.name")) + const elem = $(wd.$("modal-settings.name")); + elem.click(); browser.flushReactUpdates(); var styleObj = helper.getStyleStore(browser); @@ -153,7 +168,8 @@ describe("modals", function() { it("mapbox access token", function() { var apiKey = "testing123"; browser.setValueSafe(wd.$("modal-settings.maputnik:mapbox_access_token"), apiKey); - browser.click(wd.$("modal-settings.name")) + const elem = $(wd.$("modal-settings.name")); + elem.click(); browser.flushReactUpdates(); var styleObj = helper.getStyleStore(browser); @@ -165,7 +181,8 @@ describe("modals", function() { it("maptiler access token", function() { var apiKey = "testing123"; browser.setValueSafe(wd.$("modal-settings.maputnik:openmaptiles_access_token"), apiKey); - browser.click(wd.$("modal-settings.name")) + const elem = $(wd.$("modal-settings.name")); + elem.click(); browser.flushReactUpdates(); var styleObj = helper.getStyleStore(browser); @@ -175,7 +192,8 @@ describe("modals", function() { it("thunderforest access token", function() { var apiKey = "testing123"; browser.setValueSafe(wd.$("modal-settings.maputnik:thunderforest_access_token"), apiKey); - browser.click(wd.$("modal-settings.name")) + const elem = $(wd.$("modal-settings.name")); + elem.click(); browser.flushReactUpdates(); var styleObj = helper.getStyleStore(browser); @@ -183,9 +201,10 @@ describe("modals", function() { }) it("style renderer", function() { - var selector = wd.$("modal-settings.maputnik:renderer"); - browser.selectByValue(selector, "ol"); - browser.click(wd.$("modal-settings.name")) + const selector = $(wd.$("modal-settings.maputnik:renderer")); + selector.selectByAttribute('value', "ol"); + const elem = $(wd.$("modal-settings.name")); + elem.click(); browser.flushReactUpdates(); var styleObj = helper.getStyleStore(browser); diff --git a/test/functional/screenshots/index.js b/test/functional/screenshots/index.js index ef976c4b..e4d92886 100644 --- a/test/functional/screenshots/index.js +++ b/test/functional/screenshots/index.js @@ -8,18 +8,16 @@ var wd = require("../../wd-helper"); describe('screenshots', function() { beforeEach(function() { - browser.windowHandleSize({ - width: 1280, - height: 800 - }); + browser.setWindowSize(1280, 800) }) it("front_page", function() { browser.url(config.baseUrl+"?debug&style="+helper.getStyleUrl([ "geojson:example" ])); - browser.alertAccept(); - browser.waitForExist(".maputnik-toolbar-link"); + browser.acceptAlert(); + const elem = $(".maputnik-toolbar-link"); + elem.waitForExist(); browser.flushReactUpdates(); browser.takeScreenShot("/front_page.png") @@ -29,11 +27,13 @@ describe('screenshots', function() { browser.url(config.baseUrl+"?debug&style="+helper.getStyleUrl([ "geojson:example" ])); - browser.alertAccept(); - browser.waitForExist(".maputnik-toolbar-link"); + browser.acceptAlert(); + const elem = $(".maputnik-toolbar-link"); + elem.waitForExist(); browser.flushReactUpdates(); - browser.click(wd.$("nav:open")) + const nav_open = $(wd.$("nav:open")); + nav_open.waitForExist(); browser.flushReactUpdates(); browser.takeScreenShot("/open.png") @@ -43,11 +43,13 @@ describe('screenshots', function() { browser.url(config.baseUrl+"?debug&style="+helper.getStyleUrl([ "geojson:example" ])); - browser.alertAccept(); - browser.waitForExist(".maputnik-toolbar-link"); + browser.acceptAlert(); + const elem = $(".maputnik-toolbar-link") + elem.waitForExist() browser.flushReactUpdates(); - browser.click(wd.$("nav:export")) + const nav_export = $(wd.$("nav:export")); + nav_export.waitForExist(); browser.flushReactUpdates(); browser.takeScreenShot("/export.png") @@ -57,11 +59,13 @@ describe('screenshots', function() { browser.url(config.baseUrl+"?debug&style="+helper.getStyleUrl([ "geojson:example" ])); - browser.alertAccept(); - browser.waitForExist(".maputnik-toolbar-link"); + browser.acceptAlert(); + const elem = $(".maputnik-toolbar-link") + elem.waitForExist() browser.flushReactUpdates(); - browser.click(wd.$("nav:sources")) + const nav_sources = $(wd.$("nav:sources")); + nav_sources.waitForExist(); browser.flushReactUpdates(); browser.takeScreenShot("/sources.png") @@ -71,11 +75,13 @@ describe('screenshots', function() { browser.url(config.baseUrl+"?debug&style="+helper.getStyleUrl([ "geojson:example" ])); - browser.alertAccept(); - browser.waitForExist(".maputnik-toolbar-link"); + browser.acceptAlert(); + const elem = $(".maputnik-toolbar-link") + elem.waitForExist() browser.flushReactUpdates(); - browser.click(wd.$("nav:settings")) + const nav_settings = $(wd.$("nav:settings")); + nav_settings.waitForExist(); browser.flushReactUpdates(); browser.takeScreenShot("/settings.png") @@ -85,11 +91,14 @@ describe('screenshots', function() { browser.url(config.baseUrl+"?debug&style="+helper.getStyleUrl([ "geojson:example" ])); - browser.alertAccept(); - browser.waitForExist(".maputnik-toolbar-link"); + browser.acceptAlert(); + const elem = $(".maputnik-toolbar-link") + elem.waitForExist() browser.flushReactUpdates(); - browser.selectByValue(wd.$("nav:inspect", "select"), "inspect"); + const selectBox = $(wd.$("nav:inspect", "select")); + selectBox.selectByAttribute('value', 'inspect'); + browser.flushReactUpdates(); browser.takeScreenShot("/inspect.png") diff --git a/test/functional/util/webdriverio-ext.js b/test/functional/util/webdriverio-ext.js index 67f54f5c..658cfdc5 100644 --- a/test/functional/util/webdriverio-ext.js +++ b/test/functional/util/webdriverio-ext.js @@ -3,8 +3,8 @@ var fs = require("fs"); var path = require("path"); -browser.timeoutsAsyncScript(20*1000); -browser.timeoutsImplicitWait(20*1000); +browser.setTimeout({ 'script': 20*1000 }); +browser.setTimeout({ 'implicit': 20*1000 }); var SCREENSHOTS_PATH = artifacts.pathSync("/screenshots"); @@ -16,15 +16,18 @@ var SCREENSHOTS_PATH = artifacts.pathSync("/screenshots"); try { browser.addCommand('setValueSafe', function(selector, text) { for(var i=0; i<10; i++) { - browser.waitForVisible(selector); + const elem = $(selector); + elem.waitForDisplayed(500); - var elements = browser.elements(selector); + var elements = browser.findElements("css selector", selector); if(elements.length > 1) { throw "Too many elements found"; } - browser.setValue(selector, text); - var browserText = browser.getValue(selector); + const elem2 = $(selector); + elem2.setValue(text); + + var browserText = elem2.getValue(); if(browserText == text) { return; @@ -39,7 +42,7 @@ try { }) browser.addCommand('takeScreenShot', function(filepath) { - var data = browser.screenshot(); + var data = browser.takeScreenshot(); fs.writeFileSync(path.join(SCREENSHOTS_PATH, filepath), data.value, 'base64'); });