<commit_before>/*jshint node:true*/
var fs = require('fs');

module.exports = {
  description: 'Generates yuidoc.json if it does not exists',
  normalizeEntityName: function(){},
  afterInstall: function(){
    const pkg = this.project.pkg;
    var type = pkg.keywords && pkg.keywords.indexOf('ember-addon') === -1 ? 'app' : 'addon';
    this.generateDocOptions(this.defaultConfig(pkg), type);
    this.generateServerOptions(this.defaultConfig(pkg));
  },

  defaultConfig: function(pkg){
    return {
      name: pkg.name,
      description: pkg.description,
      version: pkg.version,
      options: {}
    };
  },

  generateDocOptions: function(config, type){
    config.options = {
      paths: [],
      enabledEnvironments: ['development', 'test'],
      exclude: 'vendor',
      outdir: './docs',
      linkNatives: true,
      quite: true,
      parseOnly: false,
      lint: false
    };
    config.options.paths.push(type);
    this.createFile('yuidoc.json', config);
  },

  generateServerOptions: function(config){
    config.options = {
      hostDocsFor: ['development', 'test'],
      port: 4462
    };
    this.createFile('yuiserver.json', config);
  },

  createFile: function(file, config){
    this.ui.writeLine('Generating ' + file + ' for ' + config.name);
    fs.writeFileSync(file , JSON.stringify(config, null, 2));
  }
};
<commit_msg>test for a sample application<commit_after>/*jshint node:true*/
var fs = require('fs');

module.exports = {
  description: 'Generates yuidoc.json if it does not exists',
  normalizeEntityName: function(){},
  afterInstall: function(){
    const pkg = this.project.pkg;
    this.ui.writeLine(pkg.keywords);
    var type = pkg.keywords && pkg.keywords.indexOf('ember-addon') !== -1 ? 'addon' : 'app';
    this.generateDocOptions(this.defaultConfig(pkg), type);
    this.generateServerOptions(this.defaultConfig(pkg));
  },

  defaultConfig: function(pkg){
    return {
      name: pkg.name,
      description: pkg.description,
      version: pkg.version,
      options: {}
    };
  },

  generateDocOptions: function(config, type){
    config.options = {
      paths: [],
      enabledEnvironments: ['development', 'test'],
      exclude: 'vendor',
      outdir: './docs',
      linkNatives: true,
      quite: true,
      parseOnly: false,
      lint: false
    };
    config.options.paths.push(type);
    this.createFile('yuidoc.json', config);
  },

  generateServerOptions: function(config){
    config.options = {
      hostDocsFor: ['development', 'test'],
      port: 4462
    };
    this.createFile('yuiserver.json', config);
  },

  createFile: function(file, config){
    this.ui.writeLine('Generating ' + file + ' for ' + config.name);
    fs.writeFileSync(file , JSON.stringify(config, null, 2));
  }
};
<|endoftext|>


<commit_before>var Rpd = Rpd;

if ((typeof Rpd === 'undefined')
 && (typeof require !== 'undefined')) {
    Rpd = require('../src/rpd.js');
}

Rpd.nodetype('spec/empty', { name: 'Mock' });
Rpd.channeltype('spec/any', { });

describe('building', function() {

// ==================== model ====================

describe('model', function() {

    it('disallows creating nodes without starting any instance of it', function() {
        expect(function() {
            // no model started at this point
            var node = new Rpd.Node('spec/empty', 'Test Node');
        }).toThrow();
    });

    it('could be started both with or without a name', function() {
        var unnamed = Rpd.Model.start();
        expect(unnamed).toBeTruthy();

        var named = Rpd.Model.start('some-name');
        expect(named).toBeTruthy();
    });

    it('accepts modifications without any renderer or target', function() {
        var model = Rpd.Model.start();
        var node = new Rpd.Node('spec/empty', 'Test Node');
        expect(node).toBeTruthy();
    });

    // ================== renderers ==================

    describe('renderer', function() {

        xit('should have an alias', function() {
            expect(function() {
                Rpd.renderer();
            }).toThrow();
        });

        it('receives no events if no target was specified', function() {

            var fooUpdateSpy = jasmine.createSpy();
            var fooRenderer = Rpd.renderer('foo', function(user_conf) {
                return fooUpdateSpy;
            });

            var barUpdateSpy = jasmine.createSpy();
            var barRenderer = Rpd.renderer('bar', function(user_conf) {
                return barUpdateSpy;
            });

            Rpd.Model.start('foo')
                     .renderWith('foo')
                     .renderWith('bar');

            expect(fooUpdateSpy).not.toHaveBeenCalled();
            expect(barUpdateSpy).not.toHaveBeenCalled();

        });

        it('receives all events, if at least one target was specified', function() {

            var fooUpdateSpy = jasmine.createSpy();
            var fooRenderer = Rpd.renderer('foo', function(user_conf) {
                return fooUpdateSpy;
            });

            var barUpdateSpy = jasmine.createSpy();
            var barRenderer = Rpd.renderer('bar', function(user_conf) {
                return barUpdateSpy;
            });

            var targetOne = {}, targetTwo = {}, targetThree = {};

            Rpd.Model.start()
                     .renderWith('foo')
                     .attachTo(targetOne)
                     .attachTo(targetTwo)
                     .renderWith('bar')
                     .attachTo(targetThree);

            expect(fooUpdateSpy).toHaveBeenCalledWith(targetOne,
                                 jasmine.objectContaining({ type: 'model/new' }));
            expect(fooUpdateSpy).toHaveBeenCalledWith(targetTwo,
                                 jasmine.objectContaining({ type: 'model/new' }));
            expect(fooUpdateSpy).toHaveBeenCalledWith(targetThree,
                                 jasmine.objectContaining({ type: 'model/new' }));

            expect(barUpdateSpy).toHaveBeenCalledWith(targetOne,
                                 jasmine.objectContaining({ type: 'model/new' }));
            expect(barUpdateSpy).toHaveBeenCalledWith(targetTwo,
                                 jasmine.objectContaining({ type: 'model/new' }));
            expect(barUpdateSpy).toHaveBeenCalledWith(targetThree,
                                 jasmine.objectContaining({ type: 'model/new' }));

        });

        it('receives configuration passed from a user', function() {
            var configurationSpy = jasmine.createSpy('conf');
            var renderer = Rpd.renderer('foo', function(user_conf) {
                configurationSpy(user_conf);
                return function() {};
            });

            var confMock = {};

            Rpd.Model.start().renderWith('foo', confMock);

            expect(configurationSpy).toHaveBeenCalledWith(confMock);
        });

        it('receives events from all started models');

    });

    // ==================== nodes ====================

    describe('node', function() {

        it('should be created with a registered type', function() {
            var renderer = Rpd.renderer('foo', function() {});
            Rpd.Model.start();
            expect(function() {
                new Rpd.Node('foo/bar');
            }).toThrow();
        });

        it('informs it was added to a model with an event', function() {
            var updateSpy = jasmine.createSpy();
            var renderer = Rpd.renderer('foo', function(user_conf) {
                return updateSpy;
            });

            Rpd.Model.start().renderWith('foo').attachTo({});

            var node = new Rpd.Node('spec/empty');

            expect(updateSpy).toHaveBeenCalledWith(
                jasmine.anything(),
                jasmine.objectContaining({ type: 'node/add',
                                           node: node })
            );
        });

        it('informs it was removed from a model with an event', function() {
            var updateSpy = jasmine.createSpy();
            var renderer = Rpd.renderer('foo', function(user_conf) {
                return updateSpy;
            });

            var model = Rpd.Model.start().renderWith('foo').attachTo({});

            var node = new Rpd.Node('spec/empty');
            model.removeNode(node);

            expect(updateSpy).toHaveBeenCalledWith(
                jasmine.anything(),
                jasmine.objectContaining({ type: 'node/remove',
                                           node: node })
            );
        });

        it('fires no events after it was removed from a model', function() {
            var updateSpy = jasmine.createSpy();
            var renderer = Rpd.renderer('foo', function(user_conf) {
                return updateSpy;
            });

            var model = Rpd.Model.start().renderWith('foo').attachTo({});

            var node = new Rpd.Node('spec/empty');
            model.removeNode(node);

            updateSpy.calls.reset();

            node.addInlet('spec/any', 'foo');

            expect(updateSpy).not.toHaveBeenCalledWith(
                jasmine.anything(),
                jasmine.objectContaining({ type: 'inlet/add' })
            );
        });

        it('informs it\'s ready when all channels were prepared');

        it('calls processing function when some new value occured or channels were modified');

        it('could be turned on and off');

    });

    // =================== channels ==================

    describe('channel', function() {

        it('informs it has been added to a node', function() {
            var updateSpy = jasmine.createSpy();
            var renderer = Rpd.renderer('foo', function(user_conf) {
                return updateSpy;
            });

            var model = Rpd.Model.start().renderWith('foo').attachTo({});

            var node = new Rpd.Node('spec/empty');

            var inlet = node.addInlet('spec/any', 'foo');

            expect(updateSpy).toHaveBeenCalledWith(
                jasmine.anything(),
                jasmine.objectContaining({ type: 'inlet/add',
                                           inlet: inlet })
            );

            var outlet = node.addOutlet('spec/any', 'foo');

            expect(updateSpy).toHaveBeenCalledWith(
                jasmine.anything(),
                jasmine.objectContaining({ type: 'outlet/add',
                                           outlet: outlet })
            );
        });

        it('informs it has been removed from a node', function() {
            var updateSpy = jasmine.createSpy();
            var renderer = Rpd.renderer('foo', function(user_conf) {
                return updateSpy;
            });

            var model = Rpd.Model.start().renderWith('foo').attachTo({});

            var node = new Rpd.Node('spec/empty');

            var inlet = node.addInlet('spec/any', 'foo');
            node.removeInlet(inlet);

            expect(updateSpy).toHaveBeenCalledWith(
                jasmine.anything(),
                jasmine.objectContaining({ type: 'inlet/remove',
                                           inlet: inlet })
            );

            var outlet = node.addOutlet('spec/any', 'foo');
            node.removeOutlet(outlet);

            expect(updateSpy).toHaveBeenCalledWith(
                jasmine.anything(),
                jasmine.objectContaining({ type: 'outlet/remove',
                                           outlet: outlet })
            );
        });

        it('stops sending values when it was removed from a node');

        it('sends default value on connection');

    });

    // ==================== links ====================

    describe('links', function() {

    });

});

});
<commit_msg>withNewModel helper<commit_after>var Rpd = Rpd;

if ((typeof Rpd === 'undefined')
 && (typeof require !== 'undefined')) {
    Rpd = require('../src/rpd.js');
}

Rpd.nodetype('spec/empty', { name: 'Mock' });
Rpd.channeltype('spec/any', { });

describe('building', function() {

// ==================== model ====================

describe('model', function() {

    it('disallows creating nodes without starting any instance of it', function() {
        expect(function() {
            // no model started at this point
            var node = new Rpd.Node('spec/empty', 'Test Node');
        }).toThrow();
    });

    it('could be started both with or without a name', function() {
        var unnamed = Rpd.Model.start();
        expect(unnamed).toBeTruthy();

        var named = Rpd.Model.start('some-name');
        expect(named).toBeTruthy();
    });

    it('accepts modifications without any renderer or target', function() {
        var model = Rpd.Model.start();
        var node = new Rpd.Node('spec/empty', 'Test Node');
        expect(node).toBeTruthy();
    });

    // ================== renderers ==================

    describe('renderer', function() {

        xit('should have an alias', function() {
            expect(function() {
                Rpd.renderer();
            }).toThrow();
        });

        it('receives no events if no target was specified', function() {

            var fooUpdateSpy = jasmine.createSpy();
            var fooRenderer = Rpd.renderer('foo', function(user_conf) {
                return fooUpdateSpy;
            });

            var barUpdateSpy = jasmine.createSpy();
            var barRenderer = Rpd.renderer('bar', function(user_conf) {
                return barUpdateSpy;
            });

            Rpd.Model.start('foo')
                     .renderWith('foo')
                     .renderWith('bar');

            expect(fooUpdateSpy).not.toHaveBeenCalled();
            expect(barUpdateSpy).not.toHaveBeenCalled();

        });

        it('receives all events, if at least one target was specified', function() {

            var fooUpdateSpy = jasmine.createSpy();
            var fooRenderer = Rpd.renderer('foo', function(user_conf) {
                return fooUpdateSpy;
            });

            var barUpdateSpy = jasmine.createSpy();
            var barRenderer = Rpd.renderer('bar', function(user_conf) {
                return barUpdateSpy;
            });

            var targetOne = {}, targetTwo = {}, targetThree = {};

            Rpd.Model.start()
                     .renderWith('foo')
                     .attachTo(targetOne)
                     .attachTo(targetTwo)
                     .renderWith('bar')
                     .attachTo(targetThree);

            expect(fooUpdateSpy).toHaveBeenCalledWith(targetOne,
                                 jasmine.objectContaining({ type: 'model/new' }));
            expect(fooUpdateSpy).toHaveBeenCalledWith(targetTwo,
                                 jasmine.objectContaining({ type: 'model/new' }));
            expect(fooUpdateSpy).toHaveBeenCalledWith(targetThree,
                                 jasmine.objectContaining({ type: 'model/new' }));

            expect(barUpdateSpy).toHaveBeenCalledWith(targetOne,
                                 jasmine.objectContaining({ type: 'model/new' }));
            expect(barUpdateSpy).toHaveBeenCalledWith(targetTwo,
                                 jasmine.objectContaining({ type: 'model/new' }));
            expect(barUpdateSpy).toHaveBeenCalledWith(targetThree,
                                 jasmine.objectContaining({ type: 'model/new' }));

        });

        it('receives configuration passed from a user', function() {
            var configurationSpy = jasmine.createSpy('conf');
            var renderer = Rpd.renderer('foo', function(user_conf) {
                configurationSpy(user_conf);
                return function() {};
            });

            var confMock = {};

            Rpd.Model.start().renderWith('foo', confMock);

            expect(configurationSpy).toHaveBeenCalledWith(confMock);
        });

        it('receives events from all started models');

    });

    function withNewModel(fn) {
        var updateSpy = jasmine.createSpy();
        var renderer = Rpd.renderer('foo', function(user_conf) {
            return updateSpy;
        });

        Rpd.Model.start().renderWith('foo').attachTo({});

        fn(updateSpy);
    }

    // ==================== nodes ====================

    describe('node', function() {

        it('should be created with a registered type', function() {
            var renderer = Rpd.renderer('foo', function() {});
            Rpd.Model.start();
            expect(function() {
                new Rpd.Node('foo/bar');
            }).toThrow();
        });

        it('informs it was added to a model with an event', function() {
            withNewModel(function(updateSpy) {
                var node = new Rpd.Node('spec/empty');

                expect(updateSpy).toHaveBeenCalledWith(
                    jasmine.anything(),
                    jasmine.objectContaining({ type: 'node/add',
                                               node: node })
                );
            });
        });

        it('informs it was removed from a model with an event', function() {
            withNewModel(function(updateSpy) {
                var node = new Rpd.Node('spec/empty');
                model.removeNode(node);

                expect(updateSpy).toHaveBeenCalledWith(
                    jasmine.anything(),
                    jasmine.objectContaining({ type: 'node/remove',
                                               node: node })
                );
            });
        });

        it('fires no events after it was removed from a model', function() {
            withNewModel(function(updateSpy) {
                var node = new Rpd.Node('spec/empty');
                model.removeNode(node);

                updateSpy.calls.reset();

                node.addInlet('spec/any', 'foo');

                expect(updateSpy).not.toHaveBeenCalledWith(
                    jasmine.anything(),
                    jasmine.objectContaining({ type: 'inlet/add' })
                );
            });
        });

        it('informs it\'s ready when all channels were prepared');

        it('calls processing function when some new value occured or channels were modified');

        it('could be turned on and off');

    });

    // =================== channels ==================

    describe('channel', function() {

        it('informs it has been added to a node', function() {
            withNewModel(function(updateSpy) {

                var node = new Rpd.Node('spec/empty');

                var inlet = node.addInlet('spec/any', 'foo');

                expect(updateSpy).toHaveBeenCalledWith(
                    jasmine.anything(),
                    jasmine.objectContaining({ type: 'inlet/add',
                                               inlet: inlet })
                );

                var outlet = node.addOutlet('spec/any', 'foo');

                expect(updateSpy).toHaveBeenCalledWith(
                    jasmine.anything(),
                    jasmine.objectContaining({ type: 'outlet/add',
                                               outlet: outlet })
                );

            );
        });

        it('informs it has been removed from a node', function() {
            withNewModel(function(updateSpy) {

                var node = new Rpd.Node('spec/empty');

                var inlet = node.addInlet('spec/any', 'foo');
                node.removeInlet(inlet);

                expect(updateSpy).toHaveBeenCalledWith(
                    jasmine.anything(),
                    jasmine.objectContaining({ type: 'inlet/remove',
                                               inlet: inlet })
                );

                var outlet = node.addOutlet('spec/any', 'foo');
                node.removeOutlet(outlet);

                expect(updateSpy).toHaveBeenCalledWith(
                    jasmine.anything(),
                    jasmine.objectContaining({ type: 'outlet/remove',
                                               outlet: outlet })
                );

            });
        });

        it('stops sending values when it was removed from a node');

        it('sends default value on connection');

    });

    // ==================== links ====================

    describe('links', function() {

    });

});

});
<|endoftext|>


<commit_before>describe("husk", function(){
  it("does something", function(){

  })
})
<commit_msg>Nope, don’t need this<commit_after><|endoftext|>


<commit_before>import mapboxgl from "mapbox-gl";
import directions from "../js/directions";
import fetchNearest from "../js/nearest";
import { setHash } from "../js/history";
import AerialToggle from "../js/aerialtogglecontrol";
import PitchToggle from "../js/pitchtogglecontrol.js";
import mapStyle from "../style/osm-liberty.json";

let template = `

`;

export default {
  name: "themap",
  template: template,
  mounted: function() {
    this.initMap();
  },
  watch: {
    "sharedState.selected.lnglat": "addMarker",
    "sharedState.show": "mapOverlay",
    "sharedState.poi": "addPOI"
  },
  methods: {
    initMap: function() {
      let _this = this;
      let mapOptions = {
        container: "map",
        style: mapStyle,
        attributionControl: false,
        center: [-80.84, 35.26],
        zoom: 8.5,
        minZoom: 8,
        maxBounds: [[-82.641, 34.115], [-79.008, 36.762]]
        //preserveDrawingBuffer: true
      };

      _this.privateState.map = new mapboxgl.Map(mapOptions);
      let map = _this.privateState.map;

      // add controls
      map.addControl(new mapboxgl.FullscreenControl(), "bottom-right");
      map.addControl(new PitchToggle({}), "bottom-right");
      map.addControl(new AerialToggle({}), "bottom-right");

      map.on("load", function() {
        // get map overlays
        _this.mapOverlay();
        // do marker if available
        if (_this.sharedState.selected.lnglat) {
          _this.addMarker();
        }
      });

      map.on("click", function(e) {
        if (map.getZoom() >= 14 && !_this.privateState.markerClicked) {
          fetchNearest(e.lngLat.lat, e.lngLat.lng, _this.sharedState);
        }
        _this.privateState.markerClicked = false;
      });
    },
    mapPitch: function() {
      this.privateState.map.getPitch() === 0
        ? this.privateState.map.easeTo({
            pitch: 90
          })
        : this.privateState.map.easeTo({
            pitch: 0,
            bearing: 0
          });
    },
    mapOverlay: function() {
      let map = this.privateState.map;
      let _this = this;

      // remove any overlays
      if (map.getLayer("overlay")) {
        map.removeLayer("overlay");
        map.removeSource("overlay");
      }

      // add overlays for tabs
      switch (_this.sharedState.show) {
        case "impervious":
          map.addLayer({
            id: "overlay",
            type: "raster",
            source: {
              type: "raster",
              tiles: [
                `https://mcmap.org/geoserver/postgis/wms?bbox={bbox-epsg-3857}&format=image/png&service=WMS&version=1.1.1&request=GetMap&srs=EPSG:3857&width=256&height=256&layers=postgis:impervious_surface&transparent=true`
              ],
              tileSize: 256,
              maxzoom: 18
            },
            minzoom: 15,
            maxzoom: 22,
            paint: {
              "raster-opacity": 1
            }
          });
          break;
        case "environment":
          map.addLayer(
            {
              id: "overlay",
              type: "raster",
              source: {
                type: "raster",
                tiles: [
                  `https://mcmap.org/geoserver/postgis/wms?bbox={bbox-epsg-3857}&format=image/png&service=WMS&version=1.1.1&request=GetMap&srs=EPSG:3857&width=256&height=256&layers=postgis:view_regulated_floodplains&transparent=true`
                ],
                tileSize: 256,
                maxzoom: 18
              },
              minzoom: 14,
              maxzoom: 22,
              paint: {
                "raster-opacity": 1
              }
            },
            "waterway_river"
          );
          break;
      }
    },
    addPOI: function() {
      let map = this.privateState.map;
      let selected = this.sharedState.selected;
      let poi = this.sharedState.poi;
      let _this = this;
      // zoom to selected and poi
      let bounds = new mapboxgl.LngLatBounds();
      bounds.extend(poi.lnglat);
      bounds.extend(selected.lnglat);
      map.fitBounds(bounds, {
        padding: 40
      });
      // remove old markers if they exist
      if (this.privateState.poiMarker) {
        this.privateState.poiMarker.remove();
      }
      // create the popup
      let popup = new mapboxgl.Popup({
        offset: [2, -23]
      }).setHTML(
        `${poi.label}
${
          poi.address
        }
Directions`
      );
      // create DOM element for the marker
      var el = document.createElement("div");
      el.classList.add("poiMarker");
      // create the marker
      this.privateState.poiMarker = new mapboxgl.Marker(el, {
        offset: [-20, -20]
      })
        .setLngLat(poi.lnglat)
        .setPopup(popup) // sets a popup on this marker
        .addTo(map);
      document
        .querySelector(".poiMarker")
        .addEventListener("click", function(e) {
          _this.privateState.markerClicked = true;
        });
    },
    addMarker: function() {
      let map = this.privateState.map;
      let selected = this.sharedState.selected;
      let _this = this;
      map.flyTo({
        center: selected.lnglat,
        zoom: 17
      });
      // push state
      setHash(selected.lnglat, _this.sharedState.show);
      // create the popup
      let popup = new mapboxgl.Popup({
        offset: [2, -23]
      }).setHTML(`<strong>${selected.label}</strong><br>${selected.address}`);
      // create DOM element for the marker
      var el = document.createElement("div");
      el.classList.add("locationMarker");
      // remove old markers if they exist
      if (this.privateState.locationMarker) {
        this.privateState.locationMarker.remove();
      }
      if (this.privateState.poiMarker) {
        this.privateState.poiMarker.remove();
      }
      // create the marker
      this.privateState.locationMarker = new mapboxgl.Marker(el, {
        offset: [-20, -20]
      })
        .setLngLat(selected.lnglat)
        .setPopup(popup) // sets a popup on this marker
        .addTo(map);
      document
        .querySelector(".locationMarker")
        .addEventListener("click", function(e) {
          _this.privateState.markerClicked = true;
        });
    }
  }
};
<commit_msg>ugly hack to fix gl js IE/Edge rendering bugs<commit_after>import mapboxgl from 'mapbox-gl';
import directions from '../js/directions';
import fetchNearest from '../js/nearest';
import {setHash} from '../js/history';
import AerialToggle from '../js/aerialtogglecontrol';
import PitchToggle from '../js/pitchtogglecontrol.js';
import mapStyle from '../style/osm-liberty.json';

let template = `

`;

function isIEorEDGE() {
  return (
    navigator.appName == 'Netscape' &&
    (navigator.appVersion.indexOf('Edge') > -1 ||
      navigator.appVersion.indexOf('Trident') > -1)
  );
}

export default {
  name: 'themap',
  template: template,
  mounted: function() {
    this.initMap();
  },
  watch: {
    'sharedState.selected.lnglat': 'addMarker',
    'sharedState.show': 'mapOverlay',
    'sharedState.poi': 'addPOI'
  },
  methods: {
    initMap: function() {
      let _this = this;
      let mapOptions = {
        container: 'map',
        style: mapStyle,
        attributionControl: false,
        center: [-80.84, 35.26],
        zoom: 8.5,
        minZoom: 8,
        maxBounds: [[-82.641, 34.115], [-79.008, 36.762]],
        preserveDrawingBuffer: true
      };

      _this.privateState.map = new mapboxgl.Map(mapOptions);
      let map = _this.privateState.map;

      // hack for IE/Edge not loading map every other load
      if (isIEorEDGE()) {
        console.log('Kicking map for Microsoft bugs...');
        map.setStyle(mapStyle);
      }

      map.on('load', function() {
        // get map overlays
        _this.mapOverlay();
        // do marker if available
        if (_this.sharedState.selected.lnglat) {
          _this.addMarker();
        }
        // add controls
        map.addControl(new mapboxgl.FullscreenControl(), 'bottom-right');
        map.addControl(new PitchToggle({}), 'bottom-right');
        map.addControl(new AerialToggle({}), 'bottom-right');
      });

      map.on('click', function(e) {
        if (map.getZoom() >= 14 && !_this.privateState.markerClicked) {
          fetchNearest(e.lngLat.lat, e.lngLat.lng, _this.sharedState);
        }
        _this.privateState.markerClicked = false;
      });
    },
    mapPitch: function() {
      this.privateState.map.getPitch() === 0
        ? this.privateState.map.easeTo({
            pitch: 90
          })
        : this.privateState.map.easeTo({
            pitch: 0,
            bearing: 0
          });
    },
    mapOverlay: function() {
      let map = this.privateState.map;
      let _this = this;

      // remove any overlays
      if (map.getLayer('overlay')) {
        map.removeLayer('overlay');
        map.removeSource('overlay');
      }

      // add overlays for tabs
      switch (_this.sharedState.show) {
        case 'impervious':
          map.addLayer({
            id: 'overlay',
            type: 'raster',
            source: {
              type: 'raster',
              tiles: [
                `https://mcmap.org/geoserver/postgis/wms?bbox={bbox-epsg-3857}&format=image/png&service=WMS&version=1.1.1&request=GetMap&srs=EPSG:3857&width=256&height=256&layers=postgis:impervious_surface&transparent=true`
              ],
              tileSize: 256,
              maxzoom: 18
            },
            minzoom: 15,
            maxzoom: 22,
            paint: {
              'raster-opacity': 1
            }
          });
          break;
        case 'environment':
          map.addLayer(
            {
              id: 'overlay',
              type: 'raster',
              source: {
                type: 'raster',
                tiles: [
                  `https://mcmap.org/geoserver/postgis/wms?bbox={bbox-epsg-3857}&format=image/png&service=WMS&version=1.1.1&request=GetMap&srs=EPSG:3857&width=256&height=256&layers=postgis:view_regulated_floodplains&transparent=true`
                ],
                tileSize: 256,
                maxzoom: 18
              },
              minzoom: 14,
              maxzoom: 22,
              paint: {
                'raster-opacity': 1
              }
            },
            'waterway_river'
          );
          break;
      }
    },
    addPOI: function() {
      let map = this.privateState.map;
      let selected = this.sharedState.selected;
      let poi = this.sharedState.poi;
      let _this = this;
      // zoom to selected and poi
      let bounds = new mapboxgl.LngLatBounds();
      bounds.extend(poi.lnglat);
      bounds.extend(selected.lnglat);
      map.fitBounds(bounds, {
        padding: 40
      });
      // remove old markers if they exist
      if (this.privateState.poiMarker) {
        this.privateState.poiMarker.remove();
      }
      // create the popup
      let popup = new mapboxgl.Popup({
        offset: [2, -23]
      }).setHTML(
        `${poi.label}
${
          poi.address
        }
Directions`
      );
      // create DOM element for the marker
      var el = document.createElement('div');
      el.classList.add('poiMarker');
      // create the marker
      this.privateState.poiMarker = new mapboxgl.Marker(el, {
        offset: [-20, -20]
      })
        .setLngLat(poi.lnglat)
        .setPopup(popup) // sets a popup on this marker
        .addTo(map);
      document
        .querySelector('.poiMarker')
        .addEventListener('click', function(e) {
          _this.privateState.markerClicked = true;
        });
    },
    addMarker: function() {
      let map = this.privateState.map;
      let selected = this.sharedState.selected;
      let _this = this;
      map.flyTo({
        center: selected.lnglat,
        zoom: 17
      });
      // push state
      setHash(selected.lnglat, _this.sharedState.show);
      // create the popup
      let popup = new mapboxgl.Popup({
        offset: [2, -23]
      }).setHTML(`<strong>${selected.label}</strong><br>${selected.address}`);
      // create DOM element for the marker
      var el = document.createElement('div');
      el.classList.add('locationMarker');
      // remove old markers if they exist
      if (this.privateState.locationMarker) {
        this.privateState.locationMarker.remove();
      }
      if (this.privateState.poiMarker) {
        this.privateState.poiMarker.remove();
      }
      // create the marker
      this.privateState.locationMarker = new mapboxgl.Marker(el, {
        offset: [-20, -20]
      })
        .setLngLat(selected.lnglat)
        .setPopup(popup) // sets a popup on this marker
        .addTo(map);
      document
        .querySelector('.locationMarker')
        .addEventListener('click', function(e) {
          _this.privateState.markerClicked = true;
        });
    }
  }
};
<|endoftext|>


<commit_before>/** @babel */

import Reporter from '../lib/reporter'
import os from 'os'
let osVersion = `${os.platform()}-${os.arch()}-${os.release()}`

let getReleaseChannel = version => {
  return (version.indexOf('beta') > -1)
    ? 'beta'
    : (version.indexOf('dev') > -1)
    ? 'dev'
    : 'stable'
}

describe("Reporter", () => {
  let reporter, requests, initialStackTraceLimit, mockActivePackages

  beforeEach(() => {
    reporter = new Reporter({
      request: (url, options) => requests.push(Object.assign({url}, options)),
      alwaysReport: true,
      reportPreviousErrors: false
    })
    requests = []
    mockActivePackages = []
    spyOn(atom.packages, 'getActivePackages').andCallFake(() => mockActivePackages)

    initialStackTraceLimit = Error.stackTraceLimit
    Error.stackTraceLimit = 1

  })

  afterEach(() => Error.stackTraceLimit = initialStackTraceLimit)

  describe(".reportUncaughtException(error)", () => {
    it("posts errors to bugsnag", () => {
      let error = new Error()
      Error.captureStackTrace(error)
      reporter.reportUncaughtException(error)
      let [lineNumber, columnNumber] = error.stack.match(/.js:(\d+):(\d+)/).slice(1).map(s => parseInt(s))

      expect(requests.length).toBe(1)
      let [request] = requests
      expect(request.method).toBe("POST")
      expect(request.url).toBe("https://notify.bugsnag.com")
      expect(request.headers.get("Content-Type")).toBe("application/json")
      let body = JSON.parse(request.body)

      // asserting the correct path is difficult on CI. let's do 'close enough'.
      expect(body.events[0].exceptions[0].stacktrace[0].file).toMatch(/reporter-spec/)
      expect(body.events[0].exceptions[0].stacktrace[0].file).not.toMatch(/\\/)
      delete body.events[0].exceptions[0].stacktrace[0].file
      delete body.events[0].exceptions[0].stacktrace[0].inProject

      expect(body).toEqual({
        "apiKey": Reporter.API_KEY,
        "notifier": {
          "name": "Atom",
          "version": Reporter.LIB_VERSION,
          "url": "https://www.atom.io"
        },
        "events": [
          {
            "payloadVersion": "2",
            "exceptions": [
              {
                "errorClass": "Error",
                "message": "",
                "stacktrace": [
                  {
                    "method": ".<anonymous>",
                    "lineNumber": lineNumber,
                    "columnNumber": columnNumber
                  }
                ]
              }
            ],
            "severity": "error",
            "user": {},
            "app": {
              "version": atom.getVersion(),
              "releaseStage": getReleaseChannel(atom.getVersion())
            },
            "device": {
              "osVersion": osVersion
            }
          }
        ]
      });})

    describe("when the error object has `privateMetadata` and `privateMetadataDescription` fields", () => {
      let [error, notification] = []

      beforeEach(() => {
        atom.notifications.clear()
        spyOn(atom.notifications, 'addInfo').andCallThrough()

        error = new Error()
        Error.captureStackTrace(error)

        error.metadata = {foo: "bar"}
        error.privateMetadata = {baz: "quux"}
        error.privateMetadataDescription = "The contents of baz"
      })

      it("posts a notification asking for consent", () => {
        reporter.reportUncaughtException(error)
        expect(atom.notifications.addInfo).toHaveBeenCalled()
      })

      it("submits the error with the private metadata if the user consents", () => {
        spyOn(reporter, 'reportUncaughtException').andCallThrough()
        reporter.reportUncaughtException(error)
        reporter.reportUncaughtException.reset()

        notification = atom.notifications.getNotifications()[0]

        let notificationOptions = atom.notifications.addInfo.argsForCall[0][1]
        expect(notificationOptions.buttons[1].text).toMatch(/Yes/)

        notificationOptions.buttons[1].onDidClick()
        expect(reporter.reportUncaughtException).toHaveBeenCalledWith(error)
        expect(reporter.reportUncaughtException.callCount).toBe(1)
        expect(error.privateMetadata).toBeUndefined()
        expect(error.privateMetadataDescription).toBeUndefined()
        expect(error.metadata).toEqual({foo: "bar", baz: "quux"})

        expect(notification.isDismissed()).toBe(true)
      })

      it("submits the error without the private metadata if the user does not consent", () => {
        spyOn(reporter, 'reportUncaughtException').andCallThrough()
        reporter.reportUncaughtException(error)
        reporter.reportUncaughtException.reset()

        notification = atom.notifications.getNotifications()[0]

        let notificationOptions = atom.notifications.addInfo.argsForCall[0][1]
        expect(notificationOptions.buttons[0].text).toMatch(/No/)

        notificationOptions.buttons[0].onDidClick()
        expect(reporter.reportUncaughtException).toHaveBeenCalledWith(error)
        expect(reporter.reportUncaughtException.callCount).toBe(1)
        expect(error.privateMetadata).toBeUndefined()
        expect(error.privateMetadataDescription).toBeUndefined()
        expect(error.metadata).toEqual({foo: "bar"})

        expect(notification.isDismissed()).toBe(true)
      })

      it("submits the error without the private metadata if the user dismisses the notification", () => {
        spyOn(reporter, 'reportUncaughtException').andCallThrough()
        reporter.reportUncaughtException(error)
        reporter.reportUncaughtException.reset()

        notification = atom.notifications.getNotifications()[0]
        notification.dismiss()

        expect(reporter.reportUncaughtException).toHaveBeenCalledWith(error)
        expect(reporter.reportUncaughtException.callCount).toBe(1)
        expect(error.privateMetadata).toBeUndefined()
        expect(error.privateMetadataDescription).toBeUndefined()
        expect(error.metadata).toEqual({foo: "bar"});});})

    it("adds bundled and user packages to the error's metadata", () => {
      mockActivePackages = [
        {name: 'user-1', path: '/Users/user/.atom/packages/user-1', metadata: {version: '1.0.0'}},
        {name: 'user-2', path: '/Users/user/.atom/packages/user-2', metadata: {version: '1.2.0'}},
        {name: 'bundled-1', path: '/Applications/Atom.app/Contents/Resources/app.asar/node_modules/bundled-1', metadata: {version: '1.0.0'}},
        {name: 'bundled-2', path: '/Applications/Atom.app/Contents/Resources/app.asar/node_modules/bundled-2', metadata: {version: '1.2.0'}},
      ]

      let error = new Error()
      Error.captureStackTrace(error)
      reporter.reportUncaughtException(error)

      expect(error.metadata.userPackages).toEqual({
        'user-1': '1.0.0',
        'user-2': '1.2.0'
      })
      expect(error.metadata.bundledPackages).toEqual({
        'bundled-1': '1.0.0',
        'bundled-2': '1.2.0'
      })
    })

    it('adds previous error messages and assertion failures to the reported metadata', () => {
      reporter.reportPreviousErrors = true

      reporter.reportUncaughtException(new Error('A'))
      reporter.reportUncaughtException(new Error('B'))
      reporter.reportFailedAssertion(new Error('X'))
      reporter.reportFailedAssertion(new Error('Y'))

      reporter.reportUncaughtException(new Error('C'))

      expect(requests.length).toBe(5)

      const lastRequest = requests[requests.length - 1]
      const body = JSON.parse(lastRequest.body)

      console.log(body);
      expect(body.events[0].metaData.previousErrors).toEqual(['A', 'B'])
      expect(body.events[0].metaData.previousAssertionFailures).toEqual(['X', 'Y'])
    })
  })

  describe(".reportFailedAssertion(error)", () => {
    it("posts warnings to bugsnag", () => {
      let error = new Error()
      Error.captureStackTrace(error)
      reporter.reportFailedAssertion(error)
      let [lineNumber, columnNumber] = error.stack.match(/.js:(\d+):(\d+)/).slice(1).map(s => parseInt(s))

      expect(requests.length).toBe(1)
      let [request] = requests
      expect(request.method).toBe("POST")
      expect(request.url).toBe("https://notify.bugsnag.com")
      expect(request.headers.get("Content-Type")).toBe("application/json")
      let body = JSON.parse(request.body)

      // asserting the correct path is difficult on CI. let's do 'close enough'.
      expect(body.events[0].exceptions[0].stacktrace[0].file).toMatch(/reporter-spec/)
      expect(body.events[0].exceptions[0].stacktrace[0].file).not.toMatch(/\\/)
      delete body.events[0].exceptions[0].stacktrace[0].file
      delete body.events[0].exceptions[0].stacktrace[0].inProject

      expect(body).toEqual({
        "apiKey": Reporter.API_KEY,
        "notifier": {
          "name": "Atom",
          "version": Reporter.LIB_VERSION,
          "url": "https://www.atom.io"
        },
        "events": [
          {
            "payloadVersion": "2",
            "exceptions": [
              {
                "errorClass": "Error",
                "message": "",
                "stacktrace": [
                  {
                    "method": ".<anonymous>",
                    "lineNumber": lineNumber,
                    "columnNumber": columnNumber
                  }
                ]
              }
            ],
            "severity": "warning",
            "user": {},
            "app": {
              "version": atom.getVersion(),
              "releaseStage": getReleaseChannel(atom.getVersion())
            },
            "device": {
              "osVersion": osVersion
            }
          }
        ]
      });})

    describe("when the error object has `privateMetadata` and `privateMetadataDescription` fields", () => {
      let [error, notification] = []

      beforeEach(() => {
        atom.notifications.clear()
        spyOn(atom.notifications, 'addInfo').andCallThrough()

        error = new Error()
        Error.captureStackTrace(error)

        error.metadata = {foo: "bar"}
        error.privateMetadata = {baz: "quux"}
        error.privateMetadataDescription = "The contents of baz"
      })

      it("posts a notification asking for consent", () => {
        reporter.reportFailedAssertion(error)
        expect(atom.notifications.addInfo).toHaveBeenCalled()
      })

      it("submits the error with the private metadata if the user consents", () => {
        spyOn(reporter, 'reportFailedAssertion').andCallThrough()
        reporter.reportFailedAssertion(error)
        reporter.reportFailedAssertion.reset()

        notification = atom.notifications.getNotifications()[0]

        let notificationOptions = atom.notifications.addInfo.argsForCall[0][1]
        expect(notificationOptions.buttons[1].text).toMatch(/Yes/)

        notificationOptions.buttons[1].onDidClick()
        expect(reporter.reportFailedAssertion).toHaveBeenCalledWith(error)
        expect(reporter.reportFailedAssertion.callCount).toBe(1)
        expect(error.privateMetadata).toBeUndefined()
        expect(error.privateMetadataDescription).toBeUndefined()
        expect(error.metadata).toEqual({foo: "bar", baz: "quux"})

        expect(notification.isDismissed()).toBe(true)
      })

      it("submits the error without the private metadata if the user does not consent", () => {
        spyOn(reporter, 'reportFailedAssertion').andCallThrough()
        reporter.reportFailedAssertion(error)
        reporter.reportFailedAssertion.reset()

        notification = atom.notifications.getNotifications()[0]

        let notificationOptions = atom.notifications.addInfo.argsForCall[0][1]
        expect(notificationOptions.buttons[0].text).toMatch(/No/)

        notificationOptions.buttons[0].onDidClick()
        expect(reporter.reportFailedAssertion).toHaveBeenCalledWith(error)
        expect(reporter.reportFailedAssertion.callCount).toBe(1)
        expect(error.privateMetadata).toBeUndefined()
        expect(error.privateMetadataDescription).toBeUndefined()
        expect(error.metadata).toEqual({foo: "bar"})

        expect(notification.isDismissed()).toBe(true)
      })

      it("submits the error without the private metadata if the user dismisses the notification", () => {
        spyOn(reporter, 'reportFailedAssertion').andCallThrough()
        reporter.reportFailedAssertion(error)
        reporter.reportFailedAssertion.reset()

        notification = atom.notifications.getNotifications()[0]
        notification.dismiss()

        expect(reporter.reportFailedAssertion).toHaveBeenCalledWith(error)
        expect(reporter.reportFailedAssertion.callCount).toBe(1)
        expect(error.privateMetadata).toBeUndefined()
        expect(error.privateMetadataDescription).toBeUndefined()
        expect(error.metadata).toEqual({foo: "bar"})
      })

      it("only notifies the user once for a given 'privateMetadataRequestName'", () => {
        let fakeStorage = {}
        spyOn(global.localStorage, 'setItem').andCallFake((key, value) => fakeStorage[key] = value)
        spyOn(global.localStorage, 'getItem').andCallFake(key => fakeStorage[key])

        error.privateMetadataRequestName = 'foo'

        reporter.reportFailedAssertion(error)
        expect(atom.notifications.addInfo).toHaveBeenCalled()
        atom.notifications.addInfo.reset()

        reporter.reportFailedAssertion(error)
        expect(atom.notifications.addInfo).not.toHaveBeenCalled()

        let error2 = new Error()
        Error.captureStackTrace(error2)
        error2.privateMetadataDescription = 'Something about you'
        error2.privateMetadata = {baz: 'quux'}
        error2.privateMetadataRequestName = 'bar'

        reporter.reportFailedAssertion(error2)
        expect(atom.notifications.addInfo).toHaveBeenCalled()
      })
    })

    it("adds bundled and user packages to the error's metadata", () => {
      mockActivePackages = [
        {name: 'user-1', path: '/Users/user/.atom/packages/user-1', metadata: {version: '1.0.0'}},
        {name: 'user-2', path: '/Users/user/.atom/packages/user-2', metadata: {version: '1.2.0'}},
        {name: 'bundled-1', path: '/Applications/Atom.app/Contents/Resources/app.asar/node_modules/bundled-1', metadata: {version: '1.0.0'}},
        {name: 'bundled-2', path: '/Applications/Atom.app/Contents/Resources/app.asar/node_modules/bundled-2', metadata: {version: '1.2.0'}},
      ]

      let error = new Error()
      Error.captureStackTrace(error)
      reporter.reportFailedAssertion(error)

      expect(error.metadata.userPackages).toEqual({
        'user-1': '1.0.0',
        'user-2': '1.2.0'
      })
      expect(error.metadata.bundledPackages).toEqual({
        'bundled-1': '1.0.0',
        'bundled-2': '1.2.0'
      })
    })

    it('adds previous error messages and assertion failures to the reported metadata', () => {
      reporter.reportPreviousErrors = true

      reporter.reportUncaughtException(new Error('A'))
      reporter.reportUncaughtException(new Error('B'))
      reporter.reportFailedAssertion(new Error('X'))
      reporter.reportFailedAssertion(new Error('Y'))

      reporter.reportFailedAssertion(new Error('C'))

      expect(requests.length).toBe(5)

      const lastRequest = requests[requests.length - 1]
      const body = JSON.parse(lastRequest.body)

      console.log(body);
      expect(body.events[0].metaData.previousErrors).toEqual(['A', 'B'])
      expect(body.events[0].metaData.previousAssertionFailures).toEqual(['X', 'Y'])
    })
  })
})
<commit_msg>Don't use babel in spec/reporter-spec.js<commit_after>const Reporter = require('../lib/reporter')
const os = require('os')
let osVersion = `${os.platform()}-${os.arch()}-${os.release()}`

let getReleaseChannel = version => {
  return (version.indexOf('beta') > -1)
    ? 'beta'
    : (version.indexOf('dev') > -1)
    ? 'dev'
    : 'stable'
}

describe("Reporter", () => {
  let reporter, requests, initialStackTraceLimit, mockActivePackages

  beforeEach(() => {
    reporter = new Reporter({
      request: (url, options) => requests.push(Object.assign({url}, options)),
      alwaysReport: true,
      reportPreviousErrors: false
    })
    requests = []
    mockActivePackages = []
    spyOn(atom.packages, 'getActivePackages').andCallFake(() => mockActivePackages)

    initialStackTraceLimit = Error.stackTraceLimit
    Error.stackTraceLimit = 1

  })

  afterEach(() => Error.stackTraceLimit = initialStackTraceLimit)

  describe(".reportUncaughtException(error)", () => {
    it("posts errors to bugsnag", () => {
      let error = new Error()
      Error.captureStackTrace(error)
      reporter.reportUncaughtException(error)
      let [lineNumber, columnNumber] = error.stack.match(/.js:(\d+):(\d+)/).slice(1).map(s => parseInt(s))

      expect(requests.length).toBe(1)
      let [request] = requests
      expect(request.method).toBe("POST")
      expect(request.url).toBe("https://notify.bugsnag.com")
      expect(request.headers.get("Content-Type")).toBe("application/json")
      let body = JSON.parse(request.body)

      // asserting the correct path is difficult on CI. let's do 'close enough'.
      expect(body.events[0].exceptions[0].stacktrace[0].file).toMatch(/reporter-spec/)
      expect(body.events[0].exceptions[0].stacktrace[0].file).not.toMatch(/\\/)
      delete body.events[0].exceptions[0].stacktrace[0].file
      delete body.events[0].exceptions[0].stacktrace[0].inProject

      expect(body).toEqual({
        "apiKey": Reporter.API_KEY,
        "notifier": {
          "name": "Atom",
          "version": Reporter.LIB_VERSION,
          "url": "https://www.atom.io"
        },
        "events": [
          {
            "payloadVersion": "2",
            "exceptions": [
              {
                "errorClass": "Error",
                "message": "",
                "stacktrace": [
                  {
                    "method": "it",
                    "lineNumber": lineNumber,
                    "columnNumber": columnNumber
                  }
                ]
              }
            ],
            "severity": "error",
            "user": {},
            "app": {
              "version": atom.getVersion(),
              "releaseStage": getReleaseChannel(atom.getVersion())
            },
            "device": {
              "osVersion": osVersion
            }
          }
        ]
      });})

    describe("when the error object has `privateMetadata` and `privateMetadataDescription` fields", () => {
      let [error, notification] = []

      beforeEach(() => {
        atom.notifications.clear()
        spyOn(atom.notifications, 'addInfo').andCallThrough()

        error = new Error()
        Error.captureStackTrace(error)

        error.metadata = {foo: "bar"}
        error.privateMetadata = {baz: "quux"}
        error.privateMetadataDescription = "The contents of baz"
      })

      it("posts a notification asking for consent", () => {
        reporter.reportUncaughtException(error)
        expect(atom.notifications.addInfo).toHaveBeenCalled()
      })

      it("submits the error with the private metadata if the user consents", () => {
        spyOn(reporter, 'reportUncaughtException').andCallThrough()
        reporter.reportUncaughtException(error)
        reporter.reportUncaughtException.reset()

        notification = atom.notifications.getNotifications()[0]

        let notificationOptions = atom.notifications.addInfo.argsForCall[0][1]
        expect(notificationOptions.buttons[1].text).toMatch(/Yes/)

        notificationOptions.buttons[1].onDidClick()
        expect(reporter.reportUncaughtException).toHaveBeenCalledWith(error)
        expect(reporter.reportUncaughtException.callCount).toBe(1)
        expect(error.privateMetadata).toBeUndefined()
        expect(error.privateMetadataDescription).toBeUndefined()
        expect(error.metadata).toEqual({foo: "bar", baz: "quux"})

        expect(notification.isDismissed()).toBe(true)
      })

      it("submits the error without the private metadata if the user does not consent", () => {
        spyOn(reporter, 'reportUncaughtException').andCallThrough()
        reporter.reportUncaughtException(error)
        reporter.reportUncaughtException.reset()

        notification = atom.notifications.getNotifications()[0]

        let notificationOptions = atom.notifications.addInfo.argsForCall[0][1]
        expect(notificationOptions.buttons[0].text).toMatch(/No/)

        notificationOptions.buttons[0].onDidClick()
        expect(reporter.reportUncaughtException).toHaveBeenCalledWith(error)
        expect(reporter.reportUncaughtException.callCount).toBe(1)
        expect(error.privateMetadata).toBeUndefined()
        expect(error.privateMetadataDescription).toBeUndefined()
        expect(error.metadata).toEqual({foo: "bar"})

        expect(notification.isDismissed()).toBe(true)
      })

      it("submits the error without the private metadata if the user dismisses the notification", () => {
        spyOn(reporter, 'reportUncaughtException').andCallThrough()
        reporter.reportUncaughtException(error)
        reporter.reportUncaughtException.reset()

        notification = atom.notifications.getNotifications()[0]
        notification.dismiss()

        expect(reporter.reportUncaughtException).toHaveBeenCalledWith(error)
        expect(reporter.reportUncaughtException.callCount).toBe(1)
        expect(error.privateMetadata).toBeUndefined()
        expect(error.privateMetadataDescription).toBeUndefined()
        expect(error.metadata).toEqual({foo: "bar"});});})

    it("adds bundled and user packages to the error's metadata", () => {
      mockActivePackages = [
        {name: 'user-1', path: '/Users/user/.atom/packages/user-1', metadata: {version: '1.0.0'}},
        {name: 'user-2', path: '/Users/user/.atom/packages/user-2', metadata: {version: '1.2.0'}},
        {name: 'bundled-1', path: '/Applications/Atom.app/Contents/Resources/app.asar/node_modules/bundled-1', metadata: {version: '1.0.0'}},
        {name: 'bundled-2', path: '/Applications/Atom.app/Contents/Resources/app.asar/node_modules/bundled-2', metadata: {version: '1.2.0'}},
      ]

      let error = new Error()
      Error.captureStackTrace(error)
      reporter.reportUncaughtException(error)

      expect(error.metadata.userPackages).toEqual({
        'user-1': '1.0.0',
        'user-2': '1.2.0'
      })
      expect(error.metadata.bundledPackages).toEqual({
        'bundled-1': '1.0.0',
        'bundled-2': '1.2.0'
      })
    })

    it('adds previous error messages and assertion failures to the reported metadata', () => {
      reporter.reportPreviousErrors = true

      reporter.reportUncaughtException(new Error('A'))
      reporter.reportUncaughtException(new Error('B'))
      reporter.reportFailedAssertion(new Error('X'))
      reporter.reportFailedAssertion(new Error('Y'))

      reporter.reportUncaughtException(new Error('C'))

      expect(requests.length).toBe(5)

      const lastRequest = requests[requests.length - 1]
      const body = JSON.parse(lastRequest.body)

      console.log(body);
      expect(body.events[0].metaData.previousErrors).toEqual(['A', 'B'])
      expect(body.events[0].metaData.previousAssertionFailures).toEqual(['X', 'Y'])
    })
  })

  describe(".reportFailedAssertion(error)", () => {
    it("posts warnings to bugsnag", () => {
      let error = new Error()
      Error.captureStackTrace(error)
      reporter.reportFailedAssertion(error)
      let [lineNumber, columnNumber] = error.stack.match(/.js:(\d+):(\d+)/).slice(1).map(s => parseInt(s))

      expect(requests.length).toBe(1)
      let [request] = requests
      expect(request.method).toBe("POST")
      expect(request.url).toBe("https://notify.bugsnag.com")
      expect(request.headers.get("Content-Type")).toBe("application/json")
      let body = JSON.parse(request.body)

      // asserting the correct path is difficult on CI. let's do 'close enough'.
      expect(body.events[0].exceptions[0].stacktrace[0].file).toMatch(/reporter-spec/)
      expect(body.events[0].exceptions[0].stacktrace[0].file).not.toMatch(/\\/)
      delete body.events[0].exceptions[0].stacktrace[0].file
      delete body.events[0].exceptions[0].stacktrace[0].inProject

      expect(body).toEqual({
        "apiKey": Reporter.API_KEY,
        "notifier": {
          "name": "Atom",
          "version": Reporter.LIB_VERSION,
          "url": "https://www.atom.io"
        },
        "events": [
          {
            "payloadVersion": "2",
            "exceptions": [
              {
                "errorClass": "Error",
                "message": "",
                "stacktrace": [
                  {
                    "method": "it",
                    "lineNumber": lineNumber,
                    "columnNumber": columnNumber
                  }
                ]
              }
            ],
            "severity": "warning",
            "user": {},
            "app": {
              "version": atom.getVersion(),
              "releaseStage": getReleaseChannel(atom.getVersion())
            },
            "device": {
              "osVersion": osVersion
            }
          }
        ]
      });})

    describe("when the error object has `privateMetadata` and `privateMetadataDescription` fields", () => {
      let [error, notification] = []

      beforeEach(() => {
        atom.notifications.clear()
        spyOn(atom.notifications, 'addInfo').andCallThrough()

        error = new Error()
        Error.captureStackTrace(error)

        error.metadata = {foo: "bar"}
        error.privateMetadata = {baz: "quux"}
        error.privateMetadataDescription = "The contents of baz"
      })

      it("posts a notification asking for consent", () => {
        reporter.reportFailedAssertion(error)
        expect(atom.notifications.addInfo).toHaveBeenCalled()
      })

      it("submits the error with the private metadata if the user consents", () => {
        spyOn(reporter, 'reportFailedAssertion').andCallThrough()
        reporter.reportFailedAssertion(error)
        reporter.reportFailedAssertion.reset()

        notification = atom.notifications.getNotifications()[0]

        let notificationOptions = atom.notifications.addInfo.argsForCall[0][1]
        expect(notificationOptions.buttons[1].text).toMatch(/Yes/)

        notificationOptions.buttons[1].onDidClick()
        expect(reporter.reportFailedAssertion).toHaveBeenCalledWith(error)
        expect(reporter.reportFailedAssertion.callCount).toBe(1)
        expect(error.privateMetadata).toBeUndefined()
        expect(error.privateMetadataDescription).toBeUndefined()
        expect(error.metadata).toEqual({foo: "bar", baz: "quux"})

        expect(notification.isDismissed()).toBe(true)
      })

      it("submits the error without the private metadata if the user does not consent", () => {
        spyOn(reporter, 'reportFailedAssertion').andCallThrough()
        reporter.reportFailedAssertion(error)
        reporter.reportFailedAssertion.reset()

        notification = atom.notifications.getNotifications()[0]

        let notificationOptions = atom.notifications.addInfo.argsForCall[0][1]
        expect(notificationOptions.buttons[0].text).toMatch(/No/)

        notificationOptions.buttons[0].onDidClick()
        expect(reporter.reportFailedAssertion).toHaveBeenCalledWith(error)
        expect(reporter.reportFailedAssertion.callCount).toBe(1)
        expect(error.privateMetadata).toBeUndefined()
        expect(error.privateMetadataDescription).toBeUndefined()
        expect(error.metadata).toEqual({foo: "bar"})

        expect(notification.isDismissed()).toBe(true)
      })

      it("submits the error without the private metadata if the user dismisses the notification", () => {
        spyOn(reporter, 'reportFailedAssertion').andCallThrough()
        reporter.reportFailedAssertion(error)
        reporter.reportFailedAssertion.reset()

        notification = atom.notifications.getNotifications()[0]
        notification.dismiss()

        expect(reporter.reportFailedAssertion).toHaveBeenCalledWith(error)
        expect(reporter.reportFailedAssertion.callCount).toBe(1)
        expect(error.privateMetadata).toBeUndefined()
        expect(error.privateMetadataDescription).toBeUndefined()
        expect(error.metadata).toEqual({foo: "bar"})
      })

      it("only notifies the user once for a given 'privateMetadataRequestName'", () => {
        let fakeStorage = {}
        spyOn(global.localStorage, 'setItem').andCallFake((key, value) => fakeStorage[key] = value)
        spyOn(global.localStorage, 'getItem').andCallFake(key => fakeStorage[key])

        error.privateMetadataRequestName = 'foo'

        reporter.reportFailedAssertion(error)
        expect(atom.notifications.addInfo).toHaveBeenCalled()
        atom.notifications.addInfo.reset()

        reporter.reportFailedAssertion(error)
        expect(atom.notifications.addInfo).not.toHaveBeenCalled()

        let error2 = new Error()
        Error.captureStackTrace(error2)
        error2.privateMetadataDescription = 'Something about you'
        error2.privateMetadata = {baz: 'quux'}
        error2.privateMetadataRequestName = 'bar'

        reporter.reportFailedAssertion(error2)
        expect(atom.notifications.addInfo).toHaveBeenCalled()
      })
    })

    it("adds bundled and user packages to the error's metadata", () => {
      mockActivePackages = [
        {name: 'user-1', path: '/Users/user/.atom/packages/user-1', metadata: {version: '1.0.0'}},
        {name: 'user-2', path: '/Users/user/.atom/packages/user-2', metadata: {version: '1.2.0'}},
        {name: 'bundled-1', path: '/Applications/Atom.app/Contents/Resources/app.asar/node_modules/bundled-1', metadata: {version: '1.0.0'}},
        {name: 'bundled-2', path: '/Applications/Atom.app/Contents/Resources/app.asar/node_modules/bundled-2', metadata: {version: '1.2.0'}},
      ]

      let error = new Error()
      Error.captureStackTrace(error)
      reporter.reportFailedAssertion(error)

      expect(error.metadata.userPackages).toEqual({
        'user-1': '1.0.0',
        'user-2': '1.2.0'
      })
      expect(error.metadata.bundledPackages).toEqual({
        'bundled-1': '1.0.0',
        'bundled-2': '1.2.0'
      })
    })

    it('adds previous error messages and assertion failures to the reported metadata', () => {
      reporter.reportPreviousErrors = true

      reporter.reportUncaughtException(new Error('A'))
      reporter.reportUncaughtException(new Error('B'))
      reporter.reportFailedAssertion(new Error('X'))
      reporter.reportFailedAssertion(new Error('Y'))

      reporter.reportFailedAssertion(new Error('C'))

      expect(requests.length).toBe(5)

      const lastRequest = requests[requests.length - 1]
      const body = JSON.parse(lastRequest.body)

      console.log(body);
      expect(body.events[0].metaData.previousErrors).toEqual(['A', 'B'])
      expect(body.events[0].metaData.previousAssertionFailures).toEqual(['X', 'Y'])
    })
  })
})
<|endoftext|>


<commit_before>var moment = require('../lib/moment-strftime');

describe('strftime', function () {
  var january17, december2;

  beforeEach(function () {
    january17 = moment('2012-01-17T19:54:20');      // Tuesday
    december2 = moment.utc('2029-12-02T01:02:03Z'); // Sunday
    farction = moment('2012-01-17T19:54:20.123');   // Tuesday with microseconds
  });

  describe('given %a', function () {
    it('gives the abbreviated weekday name ("Sun")', function () {
      expect(january17.strftime('%a')).toEqual('Tue');
    });
  });

  describe('given %A', function () {
    it('gives the  full  weekday  name ("Sunday")', function () {
      expect(january17.strftime('%A')).toEqual('Tuesday');
    });
  });

  describe('given %b', function () {
    it('gives the abbreviated month name ("Jan")', function () {
      expect(january17.strftime('%b')).toEqual('Jan');
    });
  });

  describe('given %B', function () {
    it('gives the  full  month  name ("January")', function () {
      expect(january17.strftime('%B')).toEqual('January');
    });
  });

  describe('given %d', function () {
    it('gives day of the month (01..31)', function () {
      expect(january17.strftime('%d')).toEqual('17');
      expect(december2.strftime('%d')).toEqual('02');
    });
  });

  describe('given %e', function () {
    it('gives day of the month (1..31)', function () {
      expect(january17.strftime('%e')).toEqual('17');
      expect(december2.strftime('%e')).toEqual('2');
    });
  });

  describe('given %H', function () {
    it('gives hour of the day, 24-hour clock (00..23)', function () {
      expect(january17.strftime('%H')).toEqual('19');
      expect(december2.strftime('%H')).toEqual('01');
    });
  });

  describe('given %I', function () {
    it('gives hour of the day, 12-hour clock (01..12)', function () {
      expect(january17.strftime('%I')).toEqual('07');
      expect(december2.strftime('%I')).toEqual('01');
    });
  });

  describe('given %j', function () {
    it('gives day of the year (001..366)', function () {
      expect(january17.strftime('%j')).toEqual('017');
    });
  });

  describe('given %k', function () {
    it('gives hour of the day, 24-hour clock (0..23)', function () {
      expect(january17.strftime('%k')).toEqual('19');
      expect(december2.strftime('%k')).toEqual('1');
    });
  });

  describe('given %l', function () {
    it('gives hour of the day, 12-hour clock (1..12)', function () {
      expect(january17.strftime('%l')).toEqual('7');
      expect(december2.strftime('%l')).toEqual('1');
    });
  });

  describe('given %m', function () {
    it('gives month of the year (01..12)', function () {
      expect(january17.strftime('%m')).toEqual('01');
    });
  });

  describe('given %M', function () {
    it('gives minute of the hour (00..59)', function () {
      expect(january17.strftime('%M')).toEqual('54');
    });
  });

  describe('given %p', function () {
    it('gives meridian indicator ("AM"  or  "PM")', function () {
      expect(january17.strftime('%p')).toEqual('PM');
    });
  });

  describe('given %S', function () {
    it('gives second of the minute (00..60)', function () {
      expect(january17.strftime('%S')).toEqual('20');
    });
  });

  describe('given %u', function () {
    it('gives day of the week (Sunday is 7, 1..7)', function () {
      expect(january17.strftime('%u')).toEqual('2');
      expect(december2.strftime('%u')).toEqual('7');
    });
  });

  describe('given %w', function () {
    it('gives day of the week (Sunday is 0, 0..6)', function () {
      expect(january17.strftime('%w')).toEqual('2');
      expect(december2.strftime('%w')).toEqual('0');
    });
  });

  describe('given %W', function () {
    it('gives the week number (00-53)', function () {
      expect(january17.strftime('%W')).toEqual('03');
      expect(december2.strftime('%W')).toEqual('48');
    });
  });

  describe('given %y', function () {
    it('gives year without a century (00..99)', function () {
      expect(january17.strftime('%y')).toEqual('12');
    });
  });

  describe('given %Y', function () {
    it('gives year with century', function () {
      expect(january17.strftime('%Y')).toEqual('2012');
    });
  });

  describe('given %Z', function () {
    it('gives an empty string because it is deprecated', function () {
      expect(january17.strftime('%Z')).toEqual('');
    });

    it('gives "UTC" for a moment.utc object', function () {
      expect(december2.strftime('%Z')).toEqual('UTC');
    });
  });

  describe('given %z', function () {
    it('gives the UTC offset', function () {
      expect(january17.strftime('%z')).toMatch(/[\-+]\d{4}/);
    });
  });

  describe('given %f', function () {
      it('gives the micro-seconds', function () {
          expect(farction.strftime('%f')).toEqual('123');
      });
  });

  describe('given %%', function () {
    it('gives "%" character', function () {
      expect(january17.strftime('%%')).toEqual('%');
    });
  });

  describe('given %d of month %m', function () {
    it('gives "%d of month %m"', function () {
      expect(january17.strftime('%d of month %m')).toEqual('17 of month 01');
    });
  });

  describe('given any non-parametrized text', function () {
    it('gives the same text', function () {
      expect(january17.strftime('hello world')).toEqual('hello world');
    });
  });

  it('formats correctly with a compound format', function () {
    expect(january17.strftime("%m/%d/%y %I:%M %p")).toEqual('01/17/12 07:54 PM');
  });
});
<commit_msg>Misc style fixes<commit_after>var moment = require('../lib/moment-strftime');

describe('strftime', function () {
  var january17, december2, fraction;

  beforeEach(function () {
    january17 = moment('2012-01-17T19:54:20');      // Tuesday
    december2 = moment.utc('2029-12-02T01:02:03Z'); // Sunday
    fraction = moment('2012-01-17T19:54:20.123');   // Tuesday with microseconds
  });

  describe('given %a', function () {
    it('gives the abbreviated weekday name ("Sun")', function () {
      expect(january17.strftime('%a')).toEqual('Tue');
    });
  });

  describe('given %A', function () {
    it('gives the  full  weekday  name ("Sunday")', function () {
      expect(january17.strftime('%A')).toEqual('Tuesday');
    });
  });

  describe('given %b', function () {
    it('gives the abbreviated month name ("Jan")', function () {
      expect(january17.strftime('%b')).toEqual('Jan');
    });
  });

  describe('given %B', function () {
    it('gives the  full  month  name ("January")', function () {
      expect(january17.strftime('%B')).toEqual('January');
    });
  });

  describe('given %d', function () {
    it('gives day of the month (01..31)', function () {
      expect(january17.strftime('%d')).toEqual('17');
      expect(december2.strftime('%d')).toEqual('02');
    });
  });

  describe('given %e', function () {
    it('gives day of the month (1..31)', function () {
      expect(january17.strftime('%e')).toEqual('17');
      expect(december2.strftime('%e')).toEqual('2');
    });
  });

  describe('given %H', function () {
    it('gives hour of the day, 24-hour clock (00..23)', function () {
      expect(january17.strftime('%H')).toEqual('19');
      expect(december2.strftime('%H')).toEqual('01');
    });
  });

  describe('given %I', function () {
    it('gives hour of the day, 12-hour clock (01..12)', function () {
      expect(january17.strftime('%I')).toEqual('07');
      expect(december2.strftime('%I')).toEqual('01');
    });
  });

  describe('given %j', function () {
    it('gives day of the year (001..366)', function () {
      expect(january17.strftime('%j')).toEqual('017');
    });
  });

  describe('given %k', function () {
    it('gives hour of the day, 24-hour clock (0..23)', function () {
      expect(january17.strftime('%k')).toEqual('19');
      expect(december2.strftime('%k')).toEqual('1');
    });
  });

  describe('given %l', function () {
    it('gives hour of the day, 12-hour clock (1..12)', function () {
      expect(january17.strftime('%l')).toEqual('7');
      expect(december2.strftime('%l')).toEqual('1');
    });
  });

  describe('given %m', function () {
    it('gives month of the year (01..12)', function () {
      expect(january17.strftime('%m')).toEqual('01');
    });
  });

  describe('given %M', function () {
    it('gives minute of the hour (00..59)', function () {
      expect(january17.strftime('%M')).toEqual('54');
    });
  });

  describe('given %p', function () {
    it('gives meridian indicator ("AM"  or  "PM")', function () {
      expect(january17.strftime('%p')).toEqual('PM');
    });
  });

  describe('given %S', function () {
    it('gives second of the minute (00..60)', function () {
      expect(january17.strftime('%S')).toEqual('20');
    });
  });

  describe('given %u', function () {
    it('gives day of the week (Sunday is 7, 1..7)', function () {
      expect(january17.strftime('%u')).toEqual('2');
      expect(december2.strftime('%u')).toEqual('7');
    });
  });

  describe('given %w', function () {
    it('gives day of the week (Sunday is 0, 0..6)', function () {
      expect(january17.strftime('%w')).toEqual('2');
      expect(december2.strftime('%w')).toEqual('0');
    });
  });

  describe('given %W', function () {
    it('gives the week number (00-53)', function () {
      expect(january17.strftime('%W')).toEqual('03');
      expect(december2.strftime('%W')).toEqual('48');
    });
  });

  describe('given %y', function () {
    it('gives year without a century (00..99)', function () {
      expect(january17.strftime('%y')).toEqual('12');
    });
  });

  describe('given %Y', function () {
    it('gives year with century', function () {
      expect(january17.strftime('%Y')).toEqual('2012');
    });
  });

  describe('given %Z', function () {
    it('gives an empty string because it is deprecated', function () {
      expect(january17.strftime('%Z')).toEqual('');
    });

    it('gives "UTC" for a moment.utc object', function () {
      expect(december2.strftime('%Z')).toEqual('UTC');
    });
  });

  describe('given %z', function () {
    it('gives the UTC offset', function () {
      expect(january17.strftime('%z')).toMatch(/[\-+]\d{4}/);
    });
  });

  describe('given %f', function () {
    it('gives the micro-seconds', function () {
      expect(fraction.strftime('%f')).toEqual('123');
    });
  });

  describe('given %%', function () {
    it('gives "%" character', function () {
      expect(january17.strftime('%%')).toEqual('%');
    });
  });

  describe('given %d of month %m', function () {
    it('gives "%d of month %m"', function () {
      expect(january17.strftime('%d of month %m')).toEqual('17 of month 01');
    });
  });

  describe('given any non-parametrized text', function () {
    it('gives the same text', function () {
      expect(january17.strftime('hello world')).toEqual('hello world');
    });
  });

  it('formats correctly with a compound format', function () {
    expect(january17.strftime("%m/%d/%y %I:%M %p")).toEqual('01/17/12 07:54 PM');
  });
});
<|endoftext|>


<commit_before>import { uniqueID } from '../dist/uniqueID.js'

describe( 'uniqueID', () => {
  it( 'generates unique IDs for two items in succession', () => {
    const first = uniqueID()
    const second = uniqueID()

    expect( first === second ).toBe( false )
  })

  it( 'prefixes the ID string with supplied input', () => {
    expect( uniqueID( 'prefix' ).startsWith( 'prefix-') ).toBe( true )
  })
})

<commit_msg>solving merge conflicts<commit_after>import { uniqueID } from '../dist/uniqueID.js'

describe( 'uniqueID', () => {
  it( 'generates unique IDs for two items in succession', () => {
    const first = uniqueID()
    const second = uniqueID()

    expect( first === second ).toBe( false )
  })

  it( 'prefixes the ID string with supplied input', () => {
    expect( uniqueID( 'prefix' ).startsWith( 'prefix-') ).toBe( true )
  })
})
<|endoftext|>


<commit_before>import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import { push } from 'react-router-redux';
import { connect } from 'react-redux';
import { ipcRenderer } from 'electron';
import classNames from 'classnames';
import settings from 'electron-settings';
import { Alert, Button, Intent, Overlay, Spinner } from '@blueprintjs/core';
import Feedback from '../components/common/Feedback';
import UpdateAlert from '../components/common/UpdateAlert';
import {
  LOAD_CHARTS,
  LOAD_SETTINGS,
  ON_ACCEPT_UPDATE,
  SEND_CHECKING_FOR_UPDATES,
  SEND_ERROR,
  SEND_GENERAL_ALERT,
  SEND_GIVE_FEEDBACK,
  SEND_NEEDS_UPDATE,
  SEND_REPORT_ISSUE
} from '../electron/events';
import {
  setAppSettings
} from '../actions';
import {
  loadRoundsData
} from '../components/common/Rounds/actions';
import {
  Phases
} from '../components/common/CountdownTimer/enums';

class App extends PureComponent {
  constructor() {
    super();
    this.state = {
      checkingForUpdates: false,
      generalAlertMsg: '',
      hasError: false,
      isDownloading: false,
      needsUpdate: false,
      showFeedback: false,
      showGeneralAlert: false,
      url: '',
      version: ''
    };
  }

  componentWillMount() {
    const { pushRoute } = this.props;
    ipcRenderer.on(LOAD_CHARTS, () => pushRoute('/charts'));
    ipcRenderer.on(LOAD_SETTINGS, () => pushRoute('/settings'));
    ipcRenderer.on(SEND_CHECKING_FOR_UPDATES, () => this.showCheckingForUpdates());
    ipcRenderer.on(SEND_ERROR, () => this.showError());
    ipcRenderer.on(SEND_GENERAL_ALERT, (e, msg) => this.showGeneralAlert(msg));
    ipcRenderer.on(SEND_GIVE_FEEDBACK, () => this.showSurvey('feedback'));
    ipcRenderer.on(SEND_NEEDS_UPDATE, (e, version) => {
      this.setState({ version, needsUpdate: true });
    });
    ipcRenderer.on(SEND_REPORT_ISSUE, () => this.showSurvey('issue'));
    this.loadSavedData();
  }

  showCheckingForUpdates() {
    this.setState({ checkingForUpdates: true });
  }

  showDownloadProgress() {
    this.setState({
      checkingForUpdates: false,
      isDownloading: true
    });
  }

  showGeneralAlert(msg) {
    this.setState({
      generalAlertMsg: msg,
      checkingForUpdates: false,
      isDownloading: false,
      needsUpdate: false,
      showGeneralAlert: true
    });
  }

  showError() {
    this.setState({
      checkingForUpdates: false,
      isDownloading: false,
      hasError: true,
      needsUpdate: false,
      showGeneralAlert: false,
    });
  }

  hideError() {
    this.setState({ hasError: false });
  }

  loadSavedData() {
    const {
      loadRoundsData: loadRounds,
      setAppSettings: setSettings
    } = this.props;
    const {
      rounds = {},
      system = {}
    } = settings.getAll();

    loadRounds(rounds);
    setSettings(system);
  }

  showSurvey(type) {
    const url = type === 'feedback'
      ? 'https://docs.google.com/forms/d/e/1FAIpQLSccbcfGtY6MpQeRM2hYQ-Xzji6TDKnG9Mcr_1fluDQCU0JoTA/viewform?embedded=true'
      : 'https://docs.google.com/forms/d/e/1FAIpQLSc498W0BqVHGhhb_A9WyxrHGfbMeynnuEXa5NYpjMD9nDQpng/viewform?embedded=true';

    this.setState({
      showFeedback: true,
      url
    });
  }

  onRestartLater() {
    this.setState({ needsUpdate: false });
  }

  onGeneralAlertConfirm() {
    this.setState({ showGeneralAlert: false });
  }

  closeFeedback() {
    this.setState({ showFeedback: false });
  }

  render() {
    const { currentPhase, pushRoute } = this.props;
    const {
      checkingForUpdates,
      generalAlertMsg,
      hasError,
      isDownloading,
      needsUpdate,
      showFeedback,
      showGeneralAlert,
      url,
      version
    } = this.state;
    const buttonClass = classNames({
      'pt-minimal': true,
      'btn-phase': true,
      'w-100': true,
      'bg-focus-phase': currentPhase === 0,
      'bg-break-phase': currentPhase !== 0
    });

    return (
      <main className="pt-dark bg-dark-gray-3">
        <Button
          text={Phases[currentPhase]}
          onClick={() => pushRoute('/')}
          className={buttonClass}
        />
        {this.props.children}
        <Feedback
          showFeedback={showFeedback}
          closeFeedback={() => this.closeFeedback()}
          url={url}
        />
        <UpdateAlert
          needsUpdate={needsUpdate}
          version={version}
          onRestartLater={() => this.onRestartLater()}
          onRestartNow={() => {
            this.showDownloadProgress();
            ipcRenderer.send(ON_ACCEPT_UPDATE);
          }}
        />
        <Overlay
          canEscapeKeyClose={false}
          canOutsideClickClose={false}
          isOpen={isDownloading}
          backdropClassName="bg-black"
        >
          <div
            className={`
              d-flex align-items-center justify-content-center
              w-100 h-100 bring-to-front
            `}
          >
            <Spinner
              intent={Intent.SUCCESS}
              className="w-50"
            />
          </div>
        </Overlay>
        <Overlay
          canEscapeKeyClose={false}
          canOutsideClickClose={false}
          isOpen={checkingForUpdates}
        >
          <div
            className={`
              d-flex align-items-center justify-content-center
              w-100 h-100 bring-to-front
            `}
          >
            <Spinner
              intent={Intent.SUCCESS}
              className="w-50"
            />
            Checking for updates...
          </div>
        </Overlay>
        <Alert
          isOpen={hasError}
          intent={Intent.DANGER}
          cancelButtonText="Cancel"
          confirmButtonText="Report"
          onCancel={() => this.hideError()}
          onConfirm={() => {
            this.hideError();
            this.showSurvey('issue');
          }}
        >
          Oops. Something went wrong. Please report this error.
        </Alert>
        <Alert
          isOpen={showGeneralAlert}
          intent={Intent.SUCCESS}
          onConfirm={() => this.onGeneralAlertConfirm()}
        >
          {generalAlertMsg}
        </Alert>
      </main>
    );
  }
}

App.propTypes = {
  children: PropTypes.element.isRequired,
  currentPhase: PropTypes.number.isRequired,
  loadRoundsData: PropTypes.func.isRequired,
  pushRoute: PropTypes.func.isRequired,
  setAppSettings: PropTypes.func.isRequired
};

const mapStateToProps = (state) => ({
  currentPhase: state.rounds.currentPhase
});

const mapDispatchToProps = (dispatch) => ({
  loadRoundsData: (data) => dispatch(loadRoundsData(data)),
  pushRoute: (route) => dispatch(push(route)),
  setAppSettings: (data) => dispatch(setAppSettings(data))
});

export default connect(mapStateToProps, mapDispatchToProps)(App);
<commit_msg>style: Add comments for all popups in app<commit_after>import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import { push } from 'react-router-redux';
import { connect } from 'react-redux';
import { ipcRenderer } from 'electron';
import classNames from 'classnames';
import settings from 'electron-settings';
import { Alert, Button, Intent, Overlay, Spinner } from '@blueprintjs/core';
import Feedback from '../components/common/Feedback';
import UpdateAlert from '../components/common/UpdateAlert';
import {
  LOAD_CHARTS,
  LOAD_SETTINGS,
  ON_ACCEPT_UPDATE,
  SEND_CHECKING_FOR_UPDATES,
  SEND_ERROR,
  SEND_GENERAL_ALERT,
  SEND_GIVE_FEEDBACK,
  SEND_NEEDS_UPDATE,
  SEND_REPORT_ISSUE
} from '../electron/events';
import {
  setAppSettings
} from '../actions';
import {
  loadRoundsData
} from '../components/common/Rounds/actions';
import {
  Phases
} from '../components/common/CountdownTimer/enums';

class App extends PureComponent {
  constructor() {
    super();
    this.state = {
      checkingForUpdates: false,
      generalAlertMsg: '',
      hasError: false,
      isDownloading: false,
      needsUpdate: false,
      showFeedback: false,
      showGeneralAlert: false,
      url: '',
      version: ''
    };
  }

  componentWillMount() {
    const { pushRoute } = this.props;
    ipcRenderer.on(LOAD_CHARTS, () => pushRoute('/charts'));
    ipcRenderer.on(LOAD_SETTINGS, () => pushRoute('/settings'));
    ipcRenderer.on(SEND_CHECKING_FOR_UPDATES, () => this.showCheckingForUpdates());
    ipcRenderer.on(SEND_ERROR, () => this.showError());
    ipcRenderer.on(SEND_GENERAL_ALERT, (e, msg) => this.showGeneralAlert(msg));
    ipcRenderer.on(SEND_GIVE_FEEDBACK, () => this.showSurvey('feedback'));
    ipcRenderer.on(SEND_NEEDS_UPDATE, (e, version) => {
      this.setState({ version, needsUpdate: true });
    });
    ipcRenderer.on(SEND_REPORT_ISSUE, () => this.showSurvey('issue'));
    this.loadSavedData();
  }

  showCheckingForUpdates() {
    this.setState({ checkingForUpdates: true });
  }

  showDownloadProgress() {
    this.setState({
      checkingForUpdates: false,
      isDownloading: true
    });
  }

  showGeneralAlert(msg) {
    this.setState({
      generalAlertMsg: msg,
      checkingForUpdates: false,
      isDownloading: false,
      needsUpdate: false,
      showGeneralAlert: true
    });
  }

  showError() {
    this.setState({
      checkingForUpdates: false,
      isDownloading: false,
      hasError: true,
      needsUpdate: false,
      showGeneralAlert: false,
    });
  }

  hideError() {
    this.setState({ hasError: false });
  }

  loadSavedData() {
    const {
      loadRoundsData: loadRounds,
      setAppSettings: setSettings
    } = this.props;
    const {
      rounds = {},
      system = {}
    } = settings.getAll();

    loadRounds(rounds);
    setSettings(system);
  }

  showSurvey(type) {
    const url = type === 'feedback'
      ? 'https://docs.google.com/forms/d/e/1FAIpQLSccbcfGtY6MpQeRM2hYQ-Xzji6TDKnG9Mcr_1fluDQCU0JoTA/viewform?embedded=true'
      : 'https://docs.google.com/forms/d/e/1FAIpQLSc498W0BqVHGhhb_A9WyxrHGfbMeynnuEXa5NYpjMD9nDQpng/viewform?embedded=true';

    this.setState({
      showFeedback: true,
      url
    });
  }

  onRestartLater() {
    this.setState({ needsUpdate: false });
  }

  onGeneralAlertConfirm() {
    this.setState({ showGeneralAlert: false });
  }

  closeFeedback() {
    this.setState({ showFeedback: false });
  }

  render() {
    const { currentPhase, pushRoute } = this.props;
    const {
      checkingForUpdates,
      generalAlertMsg,
      hasError,
      isDownloading,
      needsUpdate,
      showFeedback,
      showGeneralAlert,
      url,
      version
    } = this.state;
    const buttonClass = classNames({
      'pt-minimal': true,
      'btn-phase': true,
      'w-100': true,
      'bg-focus-phase': currentPhase === 0,
      'bg-break-phase': currentPhase !== 0
    });

    return (
      <main className="pt-dark bg-dark-gray-3">
        <Button
          text={Phases[currentPhase]}
          onClick={() => pushRoute('/')}
          className={buttonClass}
        />
        {this.props.children}

        {/* Feedback */}
        <Feedback
          showFeedback={showFeedback}
          closeFeedback={() => this.closeFeedback()}
          url={url}
        />

        {/* Update Alert */}
        <UpdateAlert
          needsUpdate={needsUpdate}
          version={version}
          onRestartLater={() => this.onRestartLater()}
          onRestartNow={() => {
            this.showDownloadProgress();
            ipcRenderer.send(ON_ACCEPT_UPDATE);
          }}
        />

        {/* Downloading */}
        <Overlay
          canEscapeKeyClose={false}
          canOutsideClickClose={false}
          isOpen={isDownloading}
          backdropClassName="bg-black"
        >
          <div
            className={`
              d-flex align-items-center justify-content-center
              w-100 h-100 bring-to-front
            `}
          >
            <Spinner
              intent={Intent.SUCCESS}
              className="w-50"
            />
          </div>
        </Overlay>

        {/* Checking for Updates */}
        <Overlay
          canEscapeKeyClose={false}
          canOutsideClickClose={false}
          isOpen={checkingForUpdates}
        >
          <div
            className={`
              d-flex align-items-center justify-content-center
              w-100 h-100 bring-to-front
            `}
          >
            <Spinner
              intent={Intent.SUCCESS}
              className="w-50"
            />
            Checking for updates...
          </div>
        </Overlay>

        {/* Error Alert */}
        <Alert
          isOpen={hasError}
          intent={Intent.DANGER}
          cancelButtonText="Cancel"
          confirmButtonText="Report"
          onCancel={() => this.hideError()}
          onConfirm={() => {
            this.hideError();
            this.showSurvey('issue');
          }}
        >
          Oops. Something went wrong. Please report this error.
        </Alert>

        {/* General Alert Message */}
        <Alert
          isOpen={showGeneralAlert}
          intent={Intent.SUCCESS}
          onConfirm={() => this.onGeneralAlertConfirm()}
        >
          {generalAlertMsg}
        </Alert>
      </main>
    );
  }
}

App.propTypes = {
  children: PropTypes.element.isRequired,
  currentPhase: PropTypes.number.isRequired,
  loadRoundsData: PropTypes.func.isRequired,
  pushRoute: PropTypes.func.isRequired,
  setAppSettings: PropTypes.func.isRequired
};

const mapStateToProps = (state) => ({
  currentPhase: state.rounds.currentPhase
});

const mapDispatchToProps = (dispatch) => ({
  loadRoundsData: (data) => dispatch(loadRoundsData(data)),
  pushRoute: (route) => dispatch(push(route)),
  setAppSettings: (data) => dispatch(setAppSettings(data))
});

export default connect(mapStateToProps, mapDispatchToProps)(App);
<|endoftext|>


<commit_before>import { h, Component } from 'preact';
import AppConstants from '../constants/AppConstants'
import Constants from '../constants/Constants'
const config = require('../../appconfig')

let getLatest, getByDate
if (config.mode === AppConstants.DEV_MODE) {
  getLatest = require('../../test/helpers/api_fixture').getLatest
  getByDate = require('../../test/helpers/api_fixture').getByDate
} else {
  getLatest = require('../helpers/api').getLatest
  getByDate = require('../helpers/api').getByDate
}

import Wrapper from '../components/Wrapper'

import yesterday from '../helpers/yesterday'
import tomorrow from '../helpers/tomorrow'
import normalizeDate from '../helpers/normalize-date'
import randomDate from '../helpers/random-date'

class App extends Component {
  constructor(props){
    super(props)
    this.state = {
      isLoading: true,
      tries: 0,
      isFailure: false,
      showInfo: true
    }

    this.handlePreviousClick = this.handlePreviousClick.bind(this)
    this.handleNextClick = this.handleNextClick.bind(this)
    this.handleToggleClick = this.handleToggleClick.bind(this)
    this.handleRandomClick = this.handleRandomClick.bind(this)
    this.receive = this.receive.bind(this)
  }

  receive(data){
    let date = normalizeDate(new Date(data.date))
    this.setState({
      image: data.url,
      image_hd: data.hdurl,
      title: data.title,
      explanation: data.explanation,
      date: date,
      tries: 0,
      isFailure: false,
      isLoading: false
    })
  }

  makeRequest(date){
    getByDate(date)
    .then(this.receive)
    .catch(err => {
      if (this.state.tries >= Constants.MAX_TRY)  {
        this.setState({
          isFailure: true,
          isLoading: false
        })
      } else {
        this.setState({
          tries: this.state.tries + 1
        }, this.makeRequest(yesterday(date)))
      }
    })
  }

  handleToggleClick(){
    this.setState({
      showInfo: !this.state.showInfo
    })
  }

  handlePreviousClick(){
    this.setState({
      isLoading: true
    }, this.makeRequest(yesterday(this.state.date)))
  }

  handleNextClick(){
    this.setState({
      isLoading: true
    }, this.makeRequest(tomorrow(this.state.date)))
  }

  handleRandomClick(){
    this.setState({
      isLoading: true
    }, this.makeRequest(randomDate(this.state.date)))
  }

  render() {
    return (
      <Wrapper
        isLoading={this.state.isLoading}
        isFailure={this.state.isFailure}
        onPreviousClick={this.handlePreviousClick}
        onNextClick={this.handleNextClick}
        onRandomClick={this.handleRandomClick}
        onToggleClick={this.handleToggleClick}
        image_hd={this.state.image_hd}
        title={this.state.title}
        explanation={this.state.explanation}
        date={this.state.date}
        showInfo={this.state.showInfo} />
    )
  }

  componentWillMount(){
    let today = normalizeDate(Constants.LATEST_DAY)
    this.makeRequest(today)
  }
}

export default App
<commit_msg>Load image async<commit_after>import { h, Component } from 'preact';
import AppConstants from '../constants/AppConstants'
import Constants from '../constants/Constants'
const config = require('../../appconfig')

let getLatest, getByDate
if (config.mode === AppConstants.DEV_MODE) {
  getLatest = require('../../test/helpers/api_fixture').getLatest
  getByDate = require('../../test/helpers/api_fixture').getByDate
} else {
  getLatest = require('../helpers/api').getLatest
  getByDate = require('../helpers/api').getByDate
}

import Wrapper from '../components/Wrapper'

import yesterday from '../helpers/yesterday'
import tomorrow from '../helpers/tomorrow'
import normalizeDate from '../helpers/normalize-date'
import randomDate from '../helpers/random-date'

const downloadImage = new Image()

class App extends Component {
  constructor(props){
    super(props)
    this.state = {
      isLoading: true,
      tries: 0,
      isFailure: false,
      showInfo: true
    }

    this.handlePreviousClick = this.handlePreviousClick.bind(this)
    this.handleNextClick = this.handleNextClick.bind(this)
    this.handleToggleClick = this.handleToggleClick.bind(this)
    this.handleRandomClick = this.handleRandomClick.bind(this)
    this.receive = this.receive.bind(this)
  }

  receive(data){
    let date = normalizeDate(new Date(data.date))

    downloadImage.onload = () => {
      this.setState({
        image: data.url,
        image_hd: data.hdurl,
        title: data.title,
        explanation: data.explanation,
        date: date,
        tries: 0,
        isFailure: false,
        isLoading: false
      })
    }

    downloadImage.src = data.hdurl
  }

  makeRequest(date){
    getByDate(date)
    .then(this.receive)
    .catch(err => {
      if (this.state.tries >= Constants.MAX_TRY)  {
        this.setState({
          isFailure: true,
          isLoading: false
        })
      } else {
        this.setState({
          tries: this.state.tries + 1
        }, this.makeRequest(yesterday(date)))
      }
    })
  }

  handleToggleClick(){
    this.setState({
      showInfo: !this.state.showInfo
    })
  }

  handlePreviousClick(){
    this.setState({
      isLoading: true
    }, this.makeRequest(yesterday(this.state.date)))
  }

  handleNextClick(){
    this.setState({
      isLoading: true
    }, this.makeRequest(tomorrow(this.state.date)))
  }

  handleRandomClick(){
    this.setState({
      isLoading: true
    }, this.makeRequest(randomDate(this.state.date)))
  }

  render() {
    return (
      <Wrapper
        isLoading={this.state.isLoading}
        isFailure={this.state.isFailure}
        onPreviousClick={this.handlePreviousClick}
        onNextClick={this.handleNextClick}
        onRandomClick={this.handleRandomClick}
        onToggleClick={this.handleToggleClick}
        image_hd={this.state.image_hd}
        title={this.state.title}
        explanation={this.state.explanation}
        date={this.state.date}
        showInfo={this.state.showInfo} />
    )
  }

  componentWillMount(){
    let today = normalizeDate(Constants.LATEST_DAY)
    this.makeRequest(today)
  }
}

export default App
<|endoftext|>