<commit_before>angular.module( 'celestial.actionRun', [
  'ui.state',  'ngResource'
])
.config(function config($stateProvider) {
  $stateProvider.state( 'actionRun', {
    url: '/actions/run/:id',
    views: {
     "main": {
        controller: 'actionRunCtrl',
        templateUrl: 'actions/run/run.tpl.html'
      }
    },
    data:{ pageTitle: 'Run action' }
  });
})
.factory('runService', function($location) {
  var runService = {};

  runService.run = function(ids, action) {
    runService.targets = ids;
    $location.path('/actions/run/'+action.id);
  };

  return runService;
})
.controller( 'actionRunCtrl', function actionRunController($scope, $location, actionsService, runService,$resource) {

  $scope.actionId = $location.path().replace('/actions/run/','');
  $scope.systems = [];
  $scope.userArgs= {};

  var Systems = $resource('/systems/',{} , {
    get: {method : "GET", params:{id:'@id'},url:'/systems/:id'}
  });

  _.each(runService.targets,function(id){
     Systems.get({id:id}, function (system){
       system.id = id;
       $scope.systems.push(system);
     });
  });

   $scope.loadAction = function(){
     actionsService.getAction($scope.actionId).then(function(action) {
       $scope.action = action;
       _.each(action.provided, function(a) {
          $scope.userArgs[a] = '';
       });
     });
   };

   $scope.loadAction();

   $scope.submit = function(){
    _.each(runService.targets,function(id){
      $scope.action.userArgs = $scope.userArgs;
      actionsService.launchAction(id, $scope.action);
	$location.path('/systems');
    });
   };

});
<commit_msg>grabbing action provided<commit_after>angular.module( 'celestial.actionRun', [
  'ui.state',  'ngResource'
])
.config(function config($stateProvider) {
  $stateProvider.state( 'actionRun', {
    url: '/actions/run/:id',
    views: {
     "main": {
        controller: 'actionRunCtrl',
        templateUrl: 'actions/run/run.tpl.html'
      }
    },
    data:{ pageTitle: 'Run action' }
  });
})
.factory('runService', function($location) {
  var runService = {};

  runService.run = function(ids, action) {
    runService.targets = ids;
    $location.path('/actions/run/'+action.id);
  };

  return runService;
})
.controller( 'actionRunCtrl', function actionRunController($scope, $location, actionsService, runService,$resource) {

  $scope.actionId = $location.path().replace('/actions/run/','');
  $scope.systems = [];
  $scope.userArgs= {};

  var Systems = $resource('/systems/',{} , {
    get: {method : "GET", params:{id:'@id'},url:'/systems/:id'}
  });

  _.each(runService.targets,function(id){
     Systems.get({id:id}, function (system){
       system.id = id;
       $scope.systems.push(system);
     });
  });

   $scope.loadAction = function(){
     actionsService.getAction($scope.actionId).then(function(action) {
       $scope.action = action;
       _.each($scope.action.provided, function(a) {
          $scope.userArgs[a] = '';
       });
     });
   };

   $scope.loadAction();

   $scope.submit = function(){
    _.each(runService.targets,function(id){
      $scope.action.userArgs = $scope.userArgs;
      actionsService.launchAction(id, $scope.action);
	$location.path('/systems');
    });
   };

});
<|endoftext|>


<commit_before>var graphTracer = new UndirectedGraphTracer ();
var logger = new LogTracer ();
var G = [
	[0,1,0,0,0,0],
	[1,0,0,1,1,0],
	[0,0,0,1,0,0],
	[0,1,1,0,1,1],
	[0,1,0,1,0,0],
	[0,0,0,1,0,0]
];

graphTracer._setData (G);<commit_msg>Changed the data.js file<commit_after>var graphTracer = new UndirectedGraphTracer ();
var logger = new LogTracer ();
var G = [
	[0,1,0,0,1,0],
	[1,0,0,0,1,0],
	[0,0,0,1,0,0],
	[0,0,1,0,1,1],
	[1,1,0,1,0,0],
	[0,0,0,1,0,0]
];

graphTracer._setData (G);<|endoftext|>


<commit_before>angular.module( 'ripplecharts.landing', [
  'ui.state'
])

.config(function config( $stateProvider ) {
  $stateProvider.state( 'landing', {
    url: '/',
    views: {
      "main": {
        controller: 'LandingCtrl',
        templateUrl: 'landing/landing.tpl.html'
      }
    },
    data:{ }
  });
})

.controller( 'LandingCtrl', function LandingCtrl( $scope, $rootScope, $location ) {
  //var feed = new TransactionFeed({id : 'liveFeed'});
  var api         = new ApiHandler(API),
    donut         = new ValueSummary({id:"metricDetail"}),
    exchangeRates = {};

  var valueCurrencies = {
    "USD" : "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",  //bitstamp
    "EUR" : "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",  //bitstamp
    "CNY" : "razqQKzJRdB4UxFPWf5NEpEG3WMkmwgcXA", //rippleChina
    "XRP" : ""
  }

  var totalAccounts, totalNetworkValueXRP, transactionVolumeXRP, tradeVolumeXRP;

  $scope.valueCurrency = "USD";
  $scope.valueRate;
  $scope.metricDetail      = "tradeVolume";
  $scope.metricDetailTitle = "Trade Volume (last 24 hours)";
  //dropdown to change currency for metrics
  var valueSelect = d3.select("#valueCurrency")
    .on("change", function(){
      var currency = this.value;
      setValueRate(currency, true, function(err){
        $scope.valueCurrency = currency;
        showValue("totalNetworkValue");
        showValue("transactionVolume");
        showValue("tradeVolume");
      });
    });

  valueSelect.selectAll("option")
    .data(d3.keys(valueCurrencies))
    .enter().append("option")
    .html(function(d){return d})
    .attr("selected", function(d) {if (d == $scope.valueCurrency.currency) return true});

  //remote.on('transaction_all', feed.handleTransaction); //display transaction feed
  remote.on('transaction_all', handleNewAccount); //add to new accounts total

  remote.on("connect", function(){
    getTotalAccounts();  //we want to retreive this number every time we reconnect
  });

  if (remote._connected) getTotalAccounts();

  //get "fixed" multimarket charts for the most important markets
  var markets = new MultiMarket ({
    url            : API,
    id             : "topMarkets",
    fixed          : true,
    clickable      : true,
    updateInterval : 60 //1 minute
  });

  markets.list([
    {
      base    : {currency:"XRP"},
      counter : {currency:"USD", issuer:"rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B"}
    }, {
      base    : {currency:'XRP'},
      counter : {currency:'JPY', issuer: 'rMAz5ZnK73nyNUL4foAvaxdreczCkG3vA6'}
    }, {
      base    : {currency:'BTC', issuer: 'rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q'},
      counter : {currency:'XRP'}
    }, {
      base    : {currency:"XRP"},
      counter : {currency:"CNY", issuer:"rnuF96W4SZoCJmbHYBFoJZpR8eCaxNvekK"}
    }, {
      base    : {currency:'XRP'},
      counter : {currency:'USD', issuer: 'rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q'}
    }, {
      base    : {currency:"BTC", issuer:"rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B"},
      counter : {currency:"XRP"}
    }
    ]);

  markets.on('chartClick', function(chart){
    var path = "/markets/"+chart.base.currency+
      (chart.base.issuer ? ":"+chart.base.issuer : "")+
      "/"+chart.counter.currency+
      (chart.counter.issuer ? ":"+chart.counter.issuer : "");
    $location.path(path);
    $scope.$apply();
  });

  //show the helper text the first time we visit the page
  if (!store.get("returning")) setTimeout(function(){
    d3.select("#helpButton").node().click();
  }, 100);

  $scope.$watch('metricDetail', function(){

    var ex = {rate:$scope.valueRate, currency:$scope.valueCurrency};

    if      ($scope.metricDetail == 'totalNetworkValue') {
      $scope.metricDetailTitle = "Total Network Value";
      donut.load(totalNetworkValueXRP, ex, true);
    } else if ($scope.metricDetail == 'transactionVolume') {
      $scope.metricDetailTitle = "Transaction Volume (last 24 hours)";
      donut.load(transactionVolumeXRP, ex);
    } else if ($scope.metricDetail == 'tradeVolume') {
      $scope.metricDetailTitle = "Trade Volume (last 24 hours)";
      donut.load(tradeVolumeXRP, ex);
    }
  });

  //stuff to do when leaving the page
  $scope.$on("$destroy", function(){
    markets.list([]); //this will disable the update listeners for the charts

    if (!store.get("returning") &&
      $scope.showHelp) setTimeout(function(){
        d3.select("#helpButton").node().click();
      }, 50);

    store.set("returning", true);
    clearInterval(valueInterval);
  });

  //get num accounts
  function getTotalAccounts () {
    api.getTotalAccounts(null, function(err, total){
      if (err) console.log(err);

      if (total) totalAccounts = total; //save for new account updates;
      $scope.totalAccounts = total ? commas(total) : " ";
      $scope.$apply();

    });
  }

  //look for new accounts from the websocket feed
  function handleNewAccount (tx) {
    var meta = tx.meta;
    if (meta.TransactionResult !== "tesSUCCESS") return;

    meta.AffectedNodes.forEach( function( affNode ) {

      if (affNode.CreatedNode &&
          affNode.CreatedNode.LedgerEntryType === "AccountRoot" ) {

          $scope.totalAccounts = totalAccounts ? commas(++totalAccounts) : " ";
          $scope.$apply();
      }
    });
  }

  //display the selected metric on the page, if its ready
  function showValue (metric) {
    var ex = {rate:$scope.valueRate, currency:$scope.valueCurrency},
      sign, value, precision;

  if (typeof $scope.valueRate === 'undefined') return;

    if (metric=="totalNetworkValue") {
      if (typeof totalNetworkValueXRP === 'undefined') return;
      if (metric === $scope.metricDetail) donut.load(totalNetworkValueXRP, ex, true);
      value     = totalNetworkValueXRP.total/$scope.valueRate;
      precision = 0;

    } else if (metric=="transactionVolume") {
      if (typeof transactionVolumeXRP === 'undefined') return;
      if (metric === $scope.metricDetail) donut.load(transactionVolumeXRP, ex);
      value     = transactionVolumeXRP.total/$scope.valueRate;
      precision = 2;
    } else if (metric=="tradeVolume") {
      if (typeof tradeVolumeXRP === 'undefined') return;
      if (metric === $scope.metricDetail) donut.load(tradeVolumeXRP, ex);
      value     = tradeVolumeXRP.total/$scope.valueRate;
      precision = 2;
    }

    switch ($scope.valueCurrency) {
      case "USD": sign = "$"; break;
      case "CNY": sign = "¥"; break;
      case "EUR": sign = "€"; break;
      case "XRP": sign = "";  break;
      default   : sign = "";  break;
    }

    $scope[metric] = value ? sign+commas(value, precision) : " ";
    $scope.$apply();
  }

  //get values for the various metrics
  function getValues() {

    setValueRate($scope.valueCurrency, false, function(err){
      //console.log($scope.valueRate);
      showValue("totalNetworkValue");
      showValue("transactionVolume");
      showValue("tradeVolume");
    });

    api.getNetworkValue (null, function(err, data){
      if (err) {
        console.log(err);
        data = {total:0};
      }

      totalNetworkValueXRP = data;
      showValue("totalNetworkValue");
    });

    api.getVolume24Hours(null, function(err, data){
      if (err) {
        console.log(err);
        data = {total:0};
      }

      transactionVolumeXRP = data;
      showValue("transactionVolume");
    });

    api.getTopMarkets(null, function(err, data){
      if (err) {
        console.log(err);
        data = {total:0};
      }

      tradeVolumeXRP = data;
      showValue("tradeVolume");
    });
  }

  //set the value rate for the selected currency, retreiving it from the
  //API if its not cached or if we are updating the cache
  function setValueRate (currency, useCached, callback) {
    var issuer = valueCurrencies[currency];

    if (currency == "XRP") {
      $scope.valueRate = 1;
      $scope.valueRateDisplay = "";
      return callback();
    }

    //check for cached
    if (useCached && exchangeRates[currency+"."+issuer]) {
      $scope.valueRate = exchangeRates[currency+"."+issuer];
      $scope.valueRateDisplay = commas(1/$scope.valueRate,4)  + " XRP/"+currency;
      return callback();
    }

    getExchangeRate ({
      currency : currency,
      issuer   : issuer
    }, function(err) {
      if (err) {
        console.log(err);
        $scope.valueRate = 0;
        return callback(err);
      }

      $scope.valueRate = exchangeRates[currency+"."+issuer] || 0;
      if ($scope.valueRate) $scope.valueRateDisplay = commas(1/$scope.valueRate,4) + " XRP/"+currency;
      callback();
    });
  }

  //get the exchange rate from the API
  function getExchangeRate (c, callback) {

    api.exchangeRates({
      pairs:[{
        base    : {currency : c.currency, issuer : c.issuer},
        counter : {currency:"XRP"}
      }]

    }, function(err, data){
      if (err) return callback(err);

      //cache for future reference
      data.forEach(function(d){
        exchangeRates[d.base.currency+"."+d.base.issuer] = d.rate;
      });

      callback(null, data);
    });
  }

  //get value metrics at load time and every 5 minutes
  getValues();
  var valueInterval = setInterval (getValues, 300000);
});

<commit_msg>[CHORE] JPY option for metrics, use snapswap for EUR<commit_after>angular.module( 'ripplecharts.landing', [
  'ui.state'
])

.config(function config( $stateProvider ) {
  $stateProvider.state( 'landing', {
    url: '/',
    views: {
      "main": {
        controller: 'LandingCtrl',
        templateUrl: 'landing/landing.tpl.html'
      }
    },
    data:{ }
  });
})

.controller( 'LandingCtrl', function LandingCtrl( $scope, $rootScope, $location ) {

  var api   = new ApiHandler(API);
  var donut = new ValueSummary({id:"metricDetail"});

  var exchangeRates   = {};
  var valueCurrencies = {
    "USD" : "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",  //bitstamp
    "EUR" : "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q", //snapswap
    "JPY" : "rMAz5ZnK73nyNUL4foAvaxdreczCkG3vA6", //ripple trade japan
    "CNY" : "razqQKzJRdB4UxFPWf5NEpEG3WMkmwgcXA", //rippleChina
    "XRP" : ""
  }

  var totalAccounts
  var totalNetworkValueXRP;
  var transactionVolumeXRP;
  var tradeVolumeXRP;

  $scope.valueRate;
  $scope.valueCurrency     = "USD";
  $scope.metricDetail      = "tradeVolume";
  $scope.metricDetailTitle = "Trade Volume (last 24 hours)";

  //dropdown to change currency for metrics
  var valueSelect = d3.select("#valueCurrency")
    .on("change", function(){
      var currency = this.value;
      setValueRate(currency, true, function(err){
        $scope.valueCurrency = currency;
        showValue("totalNetworkValue");
        showValue("transactionVolume");
        showValue("tradeVolume");
      });
    });

  valueSelect.selectAll("option")
    .data(d3.keys(valueCurrencies))
    .enter().append("option")
    .html(function(d){return d})
    .attr("selected", function(d) {if (d == $scope.valueCurrency.currency) return true});

  //remote.on('transaction_all', feed.handleTransaction); //display transaction feed
  remote.on('transaction_all', handleNewAccount); //add to new accounts total

  remote.on("connect", function(){
    getTotalAccounts();  //we want to retreive this number every time we reconnect
  });

  if (remote._connected) getTotalAccounts();

  //get "fixed" multimarket charts for the most important markets
  var markets = new MultiMarket ({
    url            : API,
    id             : "topMarkets",
    fixed          : true,
    clickable      : true,
    updateInterval : 60 //1 minute
  });

  markets.list([
    {
      base    : {currency:"XRP"},
      counter : {currency:"USD", issuer:"rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B"}
    }, {
      base    : {currency:'XRP'},
      counter : {currency:'JPY', issuer: 'rMAz5ZnK73nyNUL4foAvaxdreczCkG3vA6'}
    }, {
      base    : {currency:'BTC', issuer: 'rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q'},
      counter : {currency:'XRP'}
    }, {
      base    : {currency:"XRP"},
      counter : {currency:"CNY", issuer:"rnuF96W4SZoCJmbHYBFoJZpR8eCaxNvekK"}
    }, {
      base    : {currency:'XRP'},
      counter : {currency:'USD', issuer: 'rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q'}
    }, {
      base    : {currency:"BTC", issuer:"rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B"},
      counter : {currency:"XRP"}
    }
    ]);

  markets.on('chartClick', function(chart){
    var path = "/markets/"+chart.base.currency+
      (chart.base.issuer ? ":"+chart.base.issuer : "")+
      "/"+chart.counter.currency+
      (chart.counter.issuer ? ":"+chart.counter.issuer : "");
    $location.path(path);
    $scope.$apply();
  });

  //show the helper text the first time we visit the page
  if (!store.get("returning")) setTimeout(function(){
    d3.select("#helpButton").node().click();
  }, 100);

  $scope.$watch('metricDetail', function(){

    var ex = {rate:$scope.valueRate, currency:$scope.valueCurrency};

    if      ($scope.metricDetail == 'totalNetworkValue') {
      $scope.metricDetailTitle = "Total Network Value";
      donut.load(totalNetworkValueXRP, ex, true);
    } else if ($scope.metricDetail == 'transactionVolume') {
      $scope.metricDetailTitle = "Transaction Volume (last 24 hours)";
      donut.load(transactionVolumeXRP, ex);
    } else if ($scope.metricDetail == 'tradeVolume') {
      $scope.metricDetailTitle = "Trade Volume (last 24 hours)";
      donut.load(tradeVolumeXRP, ex);
    }
  });

  //stuff to do when leaving the page
  $scope.$on("$destroy", function(){
    markets.list([]); //this will disable the update listeners for the charts

    if (!store.get("returning") &&
      $scope.showHelp) setTimeout(function(){
        d3.select("#helpButton").node().click();
      }, 50);

    store.set("returning", true);
    clearInterval(valueInterval);
  });

  //get num accounts
  function getTotalAccounts () {
    api.getTotalAccounts(null, function(err, total){
      if (err) console.log(err);

      if (total) totalAccounts = total; //save for new account updates;
      $scope.totalAccounts = total ? commas(total) : " ";
      $scope.$apply();

    });
  }

  //look for new accounts from the websocket feed
  function handleNewAccount (tx) {
    var meta = tx.meta;
    if (meta.TransactionResult !== "tesSUCCESS") return;

    meta.AffectedNodes.forEach( function( affNode ) {

      if (affNode.CreatedNode &&
          affNode.CreatedNode.LedgerEntryType === "AccountRoot" ) {

          $scope.totalAccounts = totalAccounts ? commas(++totalAccounts) : " ";
          $scope.$apply();
      }
    });
  }

  //display the selected metric on the page, if its ready
  function showValue (metric) {
    var ex = {rate:$scope.valueRate, currency:$scope.valueCurrency},
      sign, value, precision;

  if (typeof $scope.valueRate === 'undefined') return;

    if (metric=="totalNetworkValue") {
      if (typeof totalNetworkValueXRP === 'undefined') return;
      if (metric === $scope.metricDetail) donut.load(totalNetworkValueXRP, ex, true);
      value     = totalNetworkValueXRP.total/$scope.valueRate;
      precision = 0;

    } else if (metric=="transactionVolume") {
      if (typeof transactionVolumeXRP === 'undefined') return;
      if (metric === $scope.metricDetail) donut.load(transactionVolumeXRP, ex);
      value     = transactionVolumeXRP.total/$scope.valueRate;
      precision = 2;
    } else if (metric=="tradeVolume") {
      if (typeof tradeVolumeXRP === 'undefined') return;
      if (metric === $scope.metricDetail) donut.load(tradeVolumeXRP, ex);
      value     = tradeVolumeXRP.total/$scope.valueRate;
      precision = 2;
    }

    switch ($scope.valueCurrency) {
      case "USD": sign = "$"; break;
      case "JPY": sign = "¥"; break;
      case "CNY": sign = "¥"; break;
      case "EUR": sign = "€"; break;
      case "XRP": sign = "";  break;
      default   : sign = "";  break;
    }

    $scope[metric] = value ? sign+commas(value, precision) : " ";
    $scope.$apply();
  }

  //get values for the various metrics
  function getValues() {

    setValueRate($scope.valueCurrency, false, function(err){
      //console.log($scope.valueRate);
      showValue("totalNetworkValue");
      showValue("transactionVolume");
      showValue("tradeVolume");
    });

    api.getNetworkValue (null, function(err, data){
      if (err) {
        console.log(err);
        data = {total:0};
      }

      totalNetworkValueXRP = data;
      showValue("totalNetworkValue");
    });

    api.getVolume24Hours(null, function(err, data){
      if (err) {
        console.log(err);
        data = {total:0};
      }

      transactionVolumeXRP = data;
      showValue("transactionVolume");
    });

    api.getTopMarkets(null, function(err, data){
      if (err) {
        console.log(err);
        data = {total:0};
      }

      tradeVolumeXRP = data;
      showValue("tradeVolume");
    });
  }

  //set the value rate for the selected currency, retreiving it from the
  //API if its not cached or if we are updating the cache
  function setValueRate (currency, useCached, callback) {
    var issuer = valueCurrencies[currency];

    if (currency == "XRP") {
      $scope.valueRate = 1;
      $scope.valueRateDisplay = "";
      return callback();
    }

    //check for cached
    if (useCached && exchangeRates[currency+"."+issuer]) {
      $scope.valueRate = exchangeRates[currency+"."+issuer];
      $scope.valueRateDisplay = commas(1/$scope.valueRate,4)  + " XRP/"+currency;
      return callback();
    }

    getExchangeRate ({
      currency : currency,
      issuer   : issuer
    }, function(err) {
      if (err) {
        console.log(err);
        $scope.valueRate = 0;
        return callback(err);
      }

      $scope.valueRate = exchangeRates[currency+"."+issuer] || 0;
      if ($scope.valueRate) $scope.valueRateDisplay = commas(1/$scope.valueRate,4) + " XRP/"+currency;
      callback();
    });
  }

  //get the exchange rate from the API
  function getExchangeRate (c, callback) {

    api.exchangeRates({
      pairs:[{
        base    : {currency : c.currency, issuer : c.issuer},
        counter : {currency:"XRP"}
      }]

    }, function(err, data){
      if (err) return callback(err);

      //cache for future reference
      data.forEach(function(d){
        exchangeRates[d.base.currency+"."+d.base.issuer] = d.rate;
      });

      callback(null, data);
    });
  }

  //get value metrics at load time and every 5 minutes
  getValues();
  var valueInterval = setInterval (getValues, 300000);
});

<|endoftext|>


<commit_before>define([
    'underscore',
    'jquery',
    'knockout',
    'arches',
    'viewmodels/card-component',
    'viewmodels/alert',
    'chosen'
], function(_, $, ko, arches, CardComponentViewModel, AlertViewModel) {

    var flattenTree = function(parents, flatList) {
        _.each(ko.unwrap(parents), function(parent) {
            flatList.push(parent);
            flattenTree(
                ko.unwrap(parent.cards),
                flatList
            );
        }, this);
        return flatList;
    };

    return ko.components.register('grouping-card-component', {
        viewModel: function(params) {
            // params.form is the CardTreeViewModel
            var self = this;
            this.saving = false;
            this.tiles = [];

            params.configKeys = ['groupedCardIds'];
            CardComponentViewModel.apply(this, [params]);

            var cards;
            if (params.state === 'report') {
                cards = flattenTree(params.pageVm.report.cards, []);
            } else {
                cards = !!params.card.parent ? params.card.parent.cards : flattenTree(params.card.topCards, []);
            }
            this.cardLookup = {};
            this.subscriptions = {};
            this.siblingCards = ko.observableArray();

            _.each(cards, function(card) {
                this.cardLookup[card.model.id] = card;
                if (card.parentCard === params.card.parentCard &&
                    card.model.cardinality() === '1' &&
                    card !== params.card &&
                    card.cards().length === 0) {
                    this.siblingCards.push({'name': card.model.name(), 'id': card.model.id});
                }
            }, this);

            this.groupedCards = ko.computed(function(){
                var gc = _.map([this.card.model.id].concat(this.groupedCardIds()), function(cardid) {
                    var card = this.cardLookup[cardid];
                    var subscription = card.model.cardinality.subscribe(function(cardinality){
                        if (cardinality !== '1') {
                            card.model.cardinality('1');
                            var errorTitle = 'Settings Conflict: Remove this card from grouped card?';
                            var errorMesssage = 'The cardinality of this card can\'t be changed until you remove it from being grouped with the "' + self.card.model.name() + '" card.  Do you want to remove this card from being grouped with the "' + self.card.model.name() + '" card';
                            params.pageVm.alert(new AlertViewModel('ep-alert-red', errorTitle, errorMesssage, function(){}, function(){
                                var newgroup = _.filter(self.groupedCardIds(), function(cardid) {
                                    return cardid !== card.model.id;
                                });
                                self.groupedCardIds(newgroup);
                                self.subscriptions[cardid].dispose();
                                card.model.cardinality('n');
                                self.card.model.save();
                            }));
                        }
                    }, this);
                    this.subscriptions[cardid] = subscription;
                    return card;
                }, this);

                return gc;
            }, this);

            if (!!params.preview) {
                _.each(this.groupedCards(), function(card) {
                    if (card.tiles().length === 0) {
                        card.tiles.push(card.getNewTile());
                    }
                    // we do this so that when you select a grouped widget
                    // the selectedCard remains the same and doesn't jump to it's true card
                    _.each(card.widgets(), function(widget) {
                        widget.parent = self.card;
                    });
                }, this);
            }

            this.groupedTiles = ko.computed(function() {
                if (this.saving) {
                    return this.tiles;
                } else {
                    var tiles = [];
                    _.each(this.groupedCards(), function(card) {
                        if (card.tiles().length > 0) {
                            tiles.push(card.tiles()[0]);
                        } else {
                            tiles.push(card.getNewTile());
                        }
                    }, this);
                    this.tiles = tiles;
                    return tiles;
                }
            }, this);

            this.getTile = function(cardid) {
                var tile = _.find(this.groupedTiles(), function(tile) {
                    return tile.parent.model.id === cardid;
                });
                if (!tile && !!params.preview) {
                    tile = self.cardLookup[cardid].getNewTile();
                }
                return tile;
            };

            this.dirty = ko.computed(function() {
                return _.find(this.groupedTiles(), function(tile) {
                    return tile.dirty();
                }, this);
            }, this);

            this.previouslySaved = ko.computed(function() {
                return !!(_.find(this.groupedTiles(), function(tile) {
                    return !!tile.tileid;
                }, this));
            }, this);

            this.saveTiles = function(){
                var self = this;
                var errors = ko.observableArray().extend({ rateLimit: 250 });
                var tiles = this.groupedTiles();
                var tile = this.groupedTiles()[0];
                this.saving = true;
                var requests = [];
                tile.save(function(response) {
                    errors.push(response);
                    self.saving = false;
                    self.groupedCardIds.valueHasMutated();
                    self.selectGroupCard();
                }, function(response){
                    var resourceInstanceId = response.resourceinstance_id;
                    var requests = _.map(_.rest(tiles), function(tile) {
                        tile.resourceinstance_id = resourceInstanceId;
                        return tile.save(function(response) {
                            errors.push(response);
                        });
                    }, self);
                    Promise.all(requests).finally(function(){
                        self.saving = false;
                        self.groupedCardIds.valueHasMutated();
                        self.selectGroupCard();
                        if (params.form.onSaveSuccess) {
                            params.form.onSaveSuccess(self.tile);
                        }
                        self.loading(false);
                    });
                });
                errors.subscribe(function(errors){
                    var title = [];
                    var message = [];
                    errors.forEach(function(response) {
                        title.push(response.responseJSON.message[0]);
                        message.push(response.responseJSON.message[1]);
                    });
                    params.pageVm.alert(new AlertViewModel('ep-alert-red', title.join(), message.join(), null, function(){}));
                    if (params.form.onSaveError) {
                        params.form.onSaveError(self.tile);
                    }
                });
            };

            this.deleteTiles = function(){
                params.loading(true);
                var self = this;
                var errors = ko.observableArray().extend({ rateLimit: 250 });

                var requests = self.groupedTiles().map(function(tile) {
                    if (!!tile.tileid) {
                        return $.ajax({
                            type: "DELETE",
                            url: arches.urls.tile,
                            data: JSON.stringify(tile.getData())
                        }).done(function(response) {
                            tile.parent.tiles.remove(tile);
                        }).fail(function(response) {
                            errors.push(response);
                        });
                    }
                }, self);

                Promise.all(requests).finally(function(){
                    params.loading(false);
                    self.selectGroupCard();
                });
                errors.subscribe(function(errors){
                    var title = [];
                    var message = [];
                    errors.forEach(function(response) {
                        title.push(response.responseJSON.message[0]);
                        message.push(response.responseJSON.message[1]);
                    });
                    params.pageVm.alert(new AlertViewModel('ep-alert-red', title.join(), message.join(), null, function(){}));
                });

            };

            this.resetTiles = function(){
                _.each(this.groupedTiles(), function(tile) {
                    tile.reset();
                }, this);
            };

            this.selectGroupCard = function() {
                this.card.selected(true);
            };

        },
        template: {
            require: 'text!templates/views/components/cards/grouping.htm'
        }
    });
});
<commit_msg>Minor refactor of grouping card to support inheritance of its view model. re #4907<commit_after>define([
    'underscore',
    'jquery',
    'knockout',
    'arches',
    'viewmodels/card-component',
    'viewmodels/alert',
    'chosen'
], function(_, $, ko, arches, CardComponentViewModel, AlertViewModel) {

    var flattenTree = function(parents, flatList) {
        _.each(ko.unwrap(parents), function(parent) {
            flatList.push(parent);
            flattenTree(
                ko.unwrap(parent.cards),
                flatList
            );
        }, this);
        return flatList;
    };

    function viewModel(params) {
        // params.form is the CardTreeViewModel
        var self = this;
        this.saving = false;
        this.tiles = [];

        params.configKeys = ['groupedCardIds'];
        CardComponentViewModel.apply(this, [params]);

        var cards;
        if (params.state === 'report') {
            cards = flattenTree(params.pageVm.report.cards, []);
        } else {
            cards = !!params.card.parent ? params.card.parent.cards : flattenTree(params.card.topCards, []);
        }
        this.cardLookup = {};
        this.subscriptions = {};
        this.siblingCards = ko.observableArray();

        _.each(cards, function(card) {
            this.cardLookup[card.model.id] = card;
            if (card.parentCard === params.card.parentCard &&
                card.model.cardinality() === '1' &&
                card !== params.card &&
                card.cards().length === 0) {
                this.siblingCards.push({'name': card.model.name(), 'id': card.model.id});
            }
        }, this);

        this.groupedCards = ko.computed(function(){
            var gc = _.map([this.card.model.id].concat(this.groupedCardIds()), function(cardid) {
                var card = this.cardLookup[cardid];
                var subscription = card.model.cardinality.subscribe(function(cardinality){
                    if (cardinality !== '1') {
                        card.model.cardinality('1');
                        var errorTitle = 'Settings Conflict: Remove this card from grouped card?';
                        var errorMesssage = 'The cardinality of this card can\'t be changed until you remove it from being grouped with the "' + self.card.model.name() + '" card.  Do you want to remove this card from being grouped with the "' + self.card.model.name() + '" card';
                        params.pageVm.alert(new AlertViewModel('ep-alert-red', errorTitle, errorMesssage, function(){}, function(){
                            var newgroup = _.filter(self.groupedCardIds(), function(cardid) {
                                return cardid !== card.model.id;
                            });
                            self.groupedCardIds(newgroup);
                            self.subscriptions[cardid].dispose();
                            card.model.cardinality('n');
                            self.card.model.save();
                        }));
                    }
                }, this);
                this.subscriptions[cardid] = subscription;
                return card;
            }, this);

            return gc;
        }, this);

        if (!!params.preview) {
            _.each(this.groupedCards(), function(card) {
                if (card.tiles().length === 0) {
                    card.tiles.push(card.getNewTile());
                }
                // we do this so that when you select a grouped widget
                // the selectedCard remains the same and doesn't jump to it's true card
                _.each(card.widgets(), function(widget) {
                    widget.parent = self.card;
                });
            }, this);
        }

        this.groupedTiles = ko.computed(function() {
            if (this.saving) {
                return this.tiles;
            } else {
                var tiles = [];
                _.each(this.groupedCards(), function(card) {
                    if (card.tiles().length > 0) {
                        tiles.push(card.tiles()[0]);
                    } else {
                        tiles.push(card.getNewTile());
                    }
                }, this);
                this.tiles = tiles;
                return tiles;
            }
        }, this);

        this.getTile = function(cardid) {
            var tile = _.find(this.groupedTiles(), function(tile) {
                return tile.parent.model.id === cardid;
            });
            if (!tile && !!params.preview) {
                tile = self.cardLookup[cardid].getNewTile();
            }
            return tile;
        };

        this.dirty = ko.computed(function() {
            return _.find(this.groupedTiles(), function(tile) {
                return tile.dirty();
            }, this);
        }, this);

        this.previouslySaved = ko.computed(function() {
            return !!(_.find(this.groupedTiles(), function(tile) {
                return !!tile.tileid;
            }, this));
        }, this);

        this.saveTiles = function(){
            var self = this;
            var errors = ko.observableArray().extend({ rateLimit: 250 });
            var tiles = this.groupedTiles();
            var tile = this.groupedTiles()[0];
            this.saving = true;
            var requests = [];
            tile.save(function(response) {
                errors.push(response);
                self.saving = false;
                self.groupedCardIds.valueHasMutated();
                self.selectGroupCard();
            }, function(response){
                var resourceInstanceId = response.resourceinstance_id;
                var requests = _.map(_.rest(tiles), function(tile) {
                    tile.resourceinstance_id = resourceInstanceId;
                    return tile.save(function(response) {
                        errors.push(response);
                    });
                }, self);
                Promise.all(requests).finally(function(){
                    self.saving = false;
                    self.groupedCardIds.valueHasMutated();
                    self.selectGroupCard();
                    if (params.form.onSaveSuccess) {
                        params.form.onSaveSuccess(self.tile);
                    }
                    self.loading(false);
                });
            });
            errors.subscribe(function(errors){
                var title = [];
                var message = [];
                errors.forEach(function(response) {
                    title.push(response.responseJSON.message[0]);
                    message.push(response.responseJSON.message[1]);
                });
                params.pageVm.alert(new AlertViewModel('ep-alert-red', title.join(), message.join(), null, function(){}));
                if (params.form.onSaveError) {
                    params.form.onSaveError(self.tile);
                }
            });
        };

        this.deleteTiles = function(){
            params.loading(true);
            var self = this;
            var errors = ko.observableArray().extend({ rateLimit: 250 });

            var requests = self.groupedTiles().map(function(tile) {
                if (!!tile.tileid) {
                    return $.ajax({
                        type: "DELETE",
                        url: arches.urls.tile,
                        data: JSON.stringify(tile.getData())
                    }).done(function(response) {
                        tile.parent.tiles.remove(tile);
                    }).fail(function(response) {
                        errors.push(response);
                    });
                }
            }, self);

            Promise.all(requests).finally(function(){
                params.loading(false);
                self.selectGroupCard();
            });
            errors.subscribe(function(errors){
                var title = [];
                var message = [];
                errors.forEach(function(response) {
                    title.push(response.responseJSON.message[0]);
                    message.push(response.responseJSON.message[1]);
                });
                params.pageVm.alert(new AlertViewModel('ep-alert-red', title.join(), message.join(), null, function(){}));
            });

        };

        this.resetTiles = function(){
            _.each(this.groupedTiles(), function(tile) {
                tile.reset();
            }, this);
        };

        this.selectGroupCard = function() {
            this.card.selected(true);
        };
    }

    ko.components.register('grouping-card-component', {
        viewModel: viewModel,
        template: {
            require: 'text!templates/views/components/cards/grouping.htm'
        }
    });
    return viewModel;
});
<|endoftext|>


<commit_before>var elasticsearch = require('elasticsearch');

if (sails.config.useElastic === true) {
    var CLIENT = new elasticsearch.Client({
        host: sails.config.elasticHost,
        log: sails.config.elasticLogLevel
    })
}

module.exports = function(req, res) {

    if (!CLIENT) {
        return res.notFound()
    }

    if (!req.body || !req.body.q || !req.socket) {
        return res.badRequest()
    }

    var query = req.body.q;

    CLIENT.search({
        index: 'search',
        from: 0,
        size: 100,
        body: {
            query: {
                query_string : {
                    fields : ["name", "id"],
                    query: query.replace(/\:/g, '\\:') + '*',
                    analyze_wildcard: true
                }
            }
        }
    }, function(err, result) {
        if (err) {
            sails.log.warn(err);
            return res.serverError()
        } else {
            console.log(result[0])
            sails.log.debug('Suggest options for %s: %d', query, result.hits.total);
            if (result.hits.total > 0) {
                return res.json(result.hits.hits)
            } else {
                return res.notFound()
            }
        }
    })

};
<commit_msg>Allow searching for HPO term ID's and match only entries that contain all of the query words (instead of one of the query words).<commit_after>var elasticsearch = require('elasticsearch');

if (sails.config.useElastic === true) {
    var CLIENT = new elasticsearch.Client({
        host: sails.config.elasticHost,
        log: sails.config.elasticLogLevel
    })
}

module.exports = function(req, res) {

    if (!CLIENT) {
        return res.notFound()
    }

    if (!req.body || !req.body.q || !req.socket) {
        return res.badRequest()
    }

    var query = req.body.q;

    CLIENT.search({
        index: 'search',
        from: 0,
        size: 100,
        body: {
            query: {
                query_string : {
                    fields : ["name", "id"],
                    query: query.replace(/\:/g, '\\\:') + '*',
                    analyze_wildcard: true,
                    default_operator: 'AND'
                }
            }
        }
    }, function(err, result) {
        if (err) {
            sails.log.warn(err);
            return res.serverError()
        } else {
            console.log(result[0])
            sails.log.debug('Suggest options for %s: %d', query, result.hits.total);
            if (result.hits.total > 0) {
                return res.json(result.hits.hits)
            } else {
                return res.notFound()
            }
        }
    })

};
<|endoftext|>


<commit_before>require('jquery');

module.exports = function() {
  var $selectableInputs = $('label[class*=block-label]');

  $selectableInputs
    .find('input[type=radio], input[type=checkbox]')
    .on('focus click', function() {

      var current = $(this).closest('label')[0];
      $(current).addClass('add-focus');
      $selectableInputs.not(current).removeClass('add-focus selected');
    })
    .on('change', function() {
      if ($(this).attr('type') === 'radio') {
        $(this).closest('label').siblings().removeClass('selected');
      }

      $(this).closest('label').toggleClass('selected', $(this).prop('checked'));
    });
};
<commit_msg>IHT-1887 Fix radio button focus<commit_after>require('jquery');

module.exports = function() {
  var $selectableInputs = $('label[class*=block-label]');

  $selectableInputs
    .find('input[type=radio], input[type=checkbox]')
    .on('focus click', function() {

      var current = $(this).closest('label')[0];
      $(current).addClass('add-focus');
      $selectableInputs.not(current).removeClass('add-focus');
    })
    .on('change', function() {
      if ($(this).attr('type') === 'radio') {
        $(this).closest('label').siblings().removeClass('selected');
      }

      $(this).closest('label').toggleClass('selected', $(this).prop('checked'));
    })
    .on('focusout', function() {
      var current = $(this).closest('label')[0];
      $(current).removeClass('add-focus');
               });
};
<|endoftext|>


<commit_before>/**
 * modules/analytics data store: accounts tests.
 *
 * Site Kit by Google, Copyright 2020 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * WordPress dependencies
 */
import apiFetch from '@wordpress/api-fetch';

/**
 * Internal dependencies
 */
import API from 'googlesitekit-api';
import { STORE_NAME } from './constants';
import {
	createTestRegistry,
	muteConsole,
	subscribeUntil,
	unsubscribeFromAll,
} from 'tests/js/utils';
import * as fixtures from './__fixtures__';

describe( 'modules/analytics accounts', () => {
	let apiFetchSpy;
	let registry;
	let store;

	beforeAll( () => {
		API.setUsingCache( false );
	} );

	beforeEach( () => {
		registry = createTestRegistry();
		store = registry.stores[ STORE_NAME ].store;

		apiFetchSpy = jest.spyOn( { apiFetch }, 'apiFetch' );
	} );

	afterAll( () => {
		API.setUsingCache( true );
	} );

	afterEach( () => {
		unsubscribeFromAll( registry );
		apiFetchSpy.mockRestore();
	} );

	describe( 'actions', () => {
		describe( 'resetAccounts', () => {
			it( 'sets accounts and related values back to their initial values', () => {
				registry.dispatch( STORE_NAME ).setSettings( {
					accountID: '12345',
					propertyID: 'UA-12345-1',
					internalWebPropertyID: '23245',
					profileID: '54321',
					useSnippet: true,
					trackingDisabled: [],
					anonymizeIP: true,
				} );
				registry.dispatch( STORE_NAME ).receiveAccounts( fixtures.accountsPropertiesProfiles.accounts );
				registry.dispatch( STORE_NAME ).receiveProperties( fixtures.accountsPropertiesProfiles.properties );
				registry.dispatch( STORE_NAME ).receiveProfiles( fixtures.accountsPropertiesProfiles.profiles );

				registry.dispatch( STORE_NAME ).resetAccounts();

				expect( registry.select( STORE_NAME ).getAccountID() ).toStrictEqual( undefined );
				expect( registry.select( STORE_NAME ).getPropertyID() ).toStrictEqual( undefined );
				expect( registry.select( STORE_NAME ).getInternalWebPropertyID() ).toStrictEqual( undefined );
				expect( registry.select( STORE_NAME ).getProfileID() ).toStrictEqual( undefined );
				muteConsole( 'error' ); // getAccounts() will trigger a request again.
				expect( registry.select( STORE_NAME ).getAccounts() ).toStrictEqual( undefined );
				// Other settings are left untouched.
				expect( registry.select( STORE_NAME ).getUseSnippet() ).toStrictEqual( true );
				expect( registry.select( STORE_NAME ).getTrackingDisabled() ).toStrictEqual( [] );
				expect( registry.select( STORE_NAME ).getAnonymizeIP() ).toStrictEqual( true );
			} );

			it( 'invalidates the resolver for getAccounts', async () => {
				registry.dispatch( STORE_NAME ).receiveAccounts( fixtures.accountsPropertiesProfiles.accounts );
				registry.select( STORE_NAME ).getAccounts();

				await subscribeUntil(
					registry,
					() => registry.select( STORE_NAME ).hasFinishedResolution( 'getAccounts' )
				);

				registry.dispatch( STORE_NAME ).resetAccounts();

				expect( registry.select( STORE_NAME ).hasFinishedResolution( 'getAccounts' ) ).toStrictEqual( false );
			} );
		} );
	} );

	describe( 'selectors', () => {
		describe( 'getAccounts', () => {
			it( 'uses a resolver to make a network request', async () => {
				registry.dispatch( STORE_NAME ).setSettings( {} );
				registry.dispatch( STORE_NAME ).receiveExistingTag( null );
				fetch
					.doMockOnceIf(
						/^\/google-site-kit\/v1\/modules\/analytics\/data\/accounts-properties-profiles/
					)
					.mockResponseOnce(
						JSON.stringify( fixtures.accountsPropertiesProfiles ),
						{ status: 200 }
					);

				const accountID = fixtures.accountsPropertiesProfiles.properties[ 0 ].accountId; // Capitalization rule exception: `accountId` is a property of an API returned value.
				const propertyID = fixtures.accountsPropertiesProfiles.profiles[ 0 ].webPropertyId; // Capitalization rule exception: `webPropertyId` is a property of an API returned value.

				const initialAccounts = registry.select( STORE_NAME ).getAccounts();

				expect( initialAccounts ).toEqual( undefined );
				await subscribeUntil( registry,
					() => (
						registry.select( STORE_NAME ).getAccounts() !== undefined
					),
				);

				const accounts = registry.select( STORE_NAME ).getAccounts();
				expect( fetch ).toHaveBeenCalledTimes( 1 );

				// Properties and profiles should also have been received by
				// this action.
				muteConsole( 'error', 2 );
				const properties = registry.select( STORE_NAME ).getProperties( accountID );
				const profiles = registry.select( STORE_NAME ).getProfiles( accountID, propertyID );

				expect( accounts ).toEqual( fixtures.accountsPropertiesProfiles.accounts );
				expect( properties ).toEqual( fixtures.accountsPropertiesProfiles.properties );
				expect( profiles ).toEqual( fixtures.accountsPropertiesProfiles.profiles );
			} );

			it( 'does not make a network request if accounts are already present', async () => {
				registry.dispatch( STORE_NAME ).setSettings( {} );
				registry.dispatch( STORE_NAME ).receiveAccounts( fixtures.accountsPropertiesProfiles.accounts );

				const accounts = registry.select( STORE_NAME ).getAccounts();

				await subscribeUntil( registry, () => registry
					.select( STORE_NAME )
					.hasFinishedResolution( 'getAccounts' )
				);

				expect( accounts ).toEqual( fixtures.accountsPropertiesProfiles.accounts );
				expect( fetch ).not.toHaveBeenCalled();
			} );

			it( 'does not make a network request if accounts exist but are empty (this is a valid state)', async () => {
				registry.dispatch( STORE_NAME ).setSettings( {} );
				registry.dispatch( STORE_NAME ).receiveAccounts( [] );

				const accounts = registry.select( STORE_NAME ).getAccounts();

				await subscribeUntil( registry, () => registry
					.select( STORE_NAME )
					.hasFinishedResolution( 'getAccounts' )
				);

				expect( accounts ).toEqual( [] );
				expect( fetch ).not.toHaveBeenCalled();
			} );

			it( 'dispatches an error if the request fails', async () => {
				const response = {
					code: 'internal_server_error',
					message: 'Internal server error',
					data: { status: 500 },
				};
				fetch
					.doMockIf(
						/^\/google-site-kit\/v1\/modules\/analytics\/data\/accounts-properties-profiles/
					)
					.mockResponse(
						JSON.stringify( response ),
						{ status: 500 }
					);

				registry.dispatch( STORE_NAME ).receiveExistingTag( null );
				registry.dispatch( STORE_NAME ).setSettings( {} );
				muteConsole( 'error' );
				registry.select( STORE_NAME ).getAccounts();
				await subscribeUntil( registry,
					// TODO: We may want a selector for this, but for now this is fine
					// because it's internal-only.
					() => store.getState().isFetchingAccountsPropertiesProfiles === false,
				);

				expect( fetch ).toHaveBeenCalledTimes( 1 );

				const accounts = registry.select( STORE_NAME ).getAccounts();
				expect( accounts ).toEqual( undefined );
			} );

			it( 'passes existing tag ID when fetching accounts', async () => {
				const existingPropertyID = 'UA-12345-1';

				registry.dispatch( STORE_NAME ).receiveExistingTag( existingPropertyID );
				registry.dispatch( STORE_NAME ).receiveTagPermission( {
					accountID: '12345',
					propertyID: existingPropertyID,
					permission: true,
				} );
				registry.dispatch( STORE_NAME ).setSettings( {} );

				fetch
					.doMockOnceIf(
						/^\/google-site-kit\/v1\/modules\/analytics\/data\/accounts-properties-profiles/
					)
					.mockResponseOnce(
						JSON.stringify( fixtures.accountsPropertiesProfiles ),
						{ status: 200 }
					);

				registry.select( STORE_NAME ).getAccounts();

				await subscribeUntil( registry,
					() => registry.select( STORE_NAME ).getAccounts() !== undefined ||
					registry.select( STORE_NAME ).getError()
				);

				// Ensure the proper parameters were sent.
				expect( fetch.mock.calls[ 0 ][ 0 ] ).toMatchQueryParameters(
					{
						existingPropertyID,
					}
				);
				expect( fetch ).toHaveBeenCalledTimes( 1 );
			} );

			it( 'sets account, property, and profile IDs in the store, if a matchedProperty is received and an account is not selected yet', async () => {
				const { accounts, properties, profiles, matchedProperty } = fixtures.accountsPropertiesProfiles;
				const matchedProfile = {
					...fixtures.profiles[ 0 ],
					id: '123456',
					webPropertyId: matchedProperty.id,
					accountId: matchedProperty.accountId,
				};
				const response = {
					accounts,
					properties,
					profiles: [
						matchedProfile,
						...profiles,
					],
					matchedProperty,
				};

				registry.dispatch( STORE_NAME ).setSettings( {} );
				registry.dispatch( STORE_NAME ).receiveExistingTag( null );

				fetch
					.doMockOnceIf(
						/^\/google-site-kit\/v1\/modules\/analytics\/data\/accounts-properties-profiles/
					)
					.mockResponseOnce(
						JSON.stringify( response ),
						{ status: 200 }
					);

				expect( store.getState().matchedProperty ).toBeFalsy();
				expect( registry.select( STORE_NAME ).getAccountID() ).toBeFalsy();
				expect( registry.select( STORE_NAME ).getPropertyID() ).toBeFalsy();
				expect( registry.select( STORE_NAME ).getInternalWebPropertyID() ).toBeFalsy();
				expect( registry.select( STORE_NAME ).getProfileID() ).toBeFalsy();

				registry.select( STORE_NAME ).getAccounts();

				await subscribeUntil( registry,
					() => (
						registry.select( STORE_NAME ).getAccounts() !== undefined
					),
				);

				expect( store.getState().matchedProperty ).toMatchObject( matchedProperty );
				expect( registry.select( STORE_NAME ).getAccountID() ).toBe( matchedProperty.accountId );
				expect( registry.select( STORE_NAME ).getPropertyID() ).toBe( matchedProperty.id );
				expect( registry.select( STORE_NAME ).getInternalWebPropertyID() ).toBe( matchedProperty.internalWebPropertyId );
				expect( registry.select( STORE_NAME ).getProfileID() ).toBe( matchedProfile.id );
			} );
		} );
	} );
} );
<commit_msg>Ensure tests run correctly after changes.<commit_after>/**
 * modules/analytics data store: accounts tests.
 *
 * Site Kit by Google, Copyright 2020 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * WordPress dependencies
 */
import apiFetch from '@wordpress/api-fetch';

/**
 * Internal dependencies
 */
import API from 'googlesitekit-api';
import { STORE_NAME } from './constants';
import {
	createTestRegistry,
	muteConsole,
	subscribeUntil,
	unsubscribeFromAll,
} from 'tests/js/utils';
import * as fixtures from './__fixtures__';

describe( 'modules/analytics accounts', () => {
	let apiFetchSpy;
	let registry;
	let store;

	const accountName = fixtures.createAccount.account.name;
	const propertyName = fixtures.createAccount.webproperty.name;
	const profileName = fixtures.createAccount.profile.name;
	const timezone = fixtures.createAccount.profile.timezone;

	beforeAll( () => {
		API.setUsingCache( false );
	} );

	beforeEach( () => {
		registry = createTestRegistry();
		store = registry.stores[ STORE_NAME ].store;

		apiFetchSpy = jest.spyOn( { apiFetch }, 'apiFetch' );
	} );

	afterAll( () => {
		API.setUsingCache( true );
	} );

	afterEach( () => {
		unsubscribeFromAll( registry );
		apiFetchSpy.mockRestore();
	} );

	describe( 'actions', () => {
		describe( 'createAccount', () => {
			it( 'creates an account ticket and sets the Terms of Service URL', async () => {
				fetch
					.doMockIf(
						/^\/google-site-kit\/v1\/modules\/analytics\/data\/create-account-ticket/
					)
					.mockResponse(
						JSON.stringify( fixtures.createAccount ),
						{ status: 200 }
					);

				muteConsole( 'error' );

				registry.dispatch( STORE_NAME ).createAccount( { accountName, propertyName, profileName, timezone } );
				await subscribeUntil( registry,
					() => (
						registry.select( STORE_NAME ).isDoingCreateAccount() === false
					),
				);

				// Ensure the proper body parameters were sent.
				expect( JSON.parse( fetch.mock.calls[ 0 ][ 1 ].body ).data ).toMatchObject(
					{ accountName, propertyName, profileName, timezone }
				);

				expect( registry.select( STORE_NAME ).getAccountTicketTermsOfServiceURL() ).toEqual( `https://analytics.google.com/analytics/web/?provisioningSignup=false#management/TermsOfService/&api.accountTicketId=${ fixtures.createAccount.id }` );
			} );

			it( 'sets isDoingCreateAccount ', async () => {
				fetch
					.doMockIf(
						/^\/google-site-kit\/v1\/modules\/analytics\/data\/create-account-ticket/
					)
					.mockResponse(
						JSON.stringify( fixtures.createAccount ),
						{ status: 200 }
					);

				registry.dispatch( STORE_NAME ).createAccount( { accountName, propertyName, profileName, timezone } );
				expect( registry.select( STORE_NAME ).isDoingCreateAccount() ).toEqual( true );
			} );

			it( 'dispatches an error if the request fails ', async () => {
				const response = {
					code: 'internal_server_error',
					message: 'Internal server error',
					data: { status: 500 },
				};

				fetch
					.doMockIf(
						/^\/google-site-kit\/v1\/modules\/analytics\/data\/create-account-ticket/
					)
					.mockResponse(
						JSON.stringify( response ),
						{ status: 500 }
					);

				muteConsole( 'error' );

				registry.dispatch( STORE_NAME ).createAccount( { accountName, propertyName, profileName, timezone } );

				await subscribeUntil( registry,
					() => (
						registry.select( STORE_NAME ).isDoingCreateAccount() === false
					),
				);

				expect( registry.select( STORE_NAME ).getError() ).toMatchObject( response );
			} );
		} );

		describe( 'resetAccounts', () => {
			it( 'sets accounts and related values back to their initial values', () => {
				registry.dispatch( STORE_NAME ).setSettings( {
					accountID: '12345',
					propertyID: 'UA-12345-1',
					internalWebPropertyID: '23245',
					profileID: '54321',
					useSnippet: true,
					trackingDisabled: [],
					anonymizeIP: true,
				} );
				registry.dispatch( STORE_NAME ).receiveAccounts( fixtures.accountsPropertiesProfiles.accounts );
				registry.dispatch( STORE_NAME ).receiveProperties( fixtures.accountsPropertiesProfiles.properties );
				registry.dispatch( STORE_NAME ).receiveProfiles( fixtures.accountsPropertiesProfiles.profiles );

				registry.dispatch( STORE_NAME ).resetAccounts();

				expect( registry.select( STORE_NAME ).getAccountID() ).toStrictEqual( undefined );
				expect( registry.select( STORE_NAME ).getPropertyID() ).toStrictEqual( undefined );
				expect( registry.select( STORE_NAME ).getInternalWebPropertyID() ).toStrictEqual( undefined );
				expect( registry.select( STORE_NAME ).getProfileID() ).toStrictEqual( undefined );
				muteConsole( 'error' ); // getAccounts() will trigger a request again.
				expect( registry.select( STORE_NAME ).getAccounts() ).toStrictEqual( undefined );
				// Other settings are left untouched.
				expect( registry.select( STORE_NAME ).getUseSnippet() ).toStrictEqual( true );
				expect( registry.select( STORE_NAME ).getTrackingDisabled() ).toStrictEqual( [] );
				expect( registry.select( STORE_NAME ).getAnonymizeIP() ).toStrictEqual( true );
			} );

			it( 'invalidates the resolver for getAccounts', async () => {
				registry.dispatch( STORE_NAME ).receiveAccounts( fixtures.accountsPropertiesProfiles.accounts );
				registry.select( STORE_NAME ).getAccounts();

				await subscribeUntil(
					registry,
					() => registry.select( STORE_NAME ).hasFinishedResolution( 'getAccounts' )
				);

				registry.dispatch( STORE_NAME ).resetAccounts();

				expect( registry.select( STORE_NAME ).hasFinishedResolution( 'getAccounts' ) ).toStrictEqual( false );
			} );
		} );
	} );

	describe( 'selectors', () => {
		describe( 'getAccounts', () => {
			it( 'uses a resolver to make a network request', async () => {
				registry.dispatch( STORE_NAME ).setSettings( {} );
				registry.dispatch( STORE_NAME ).receiveExistingTag( null );
				fetch
					.doMockOnceIf(
						/^\/google-site-kit\/v1\/modules\/analytics\/data\/accounts-properties-profiles/
					)
					.mockResponseOnce(
						JSON.stringify( fixtures.accountsPropertiesProfiles ),
						{ status: 200 }
					);

				const accountID = fixtures.accountsPropertiesProfiles.properties[ 0 ].accountId; // Capitalization rule exception: `accountId` is a property of an API returned value.
				const propertyID = fixtures.accountsPropertiesProfiles.profiles[ 0 ].webPropertyId; // Capitalization rule exception: `webPropertyId` is a property of an API returned value.

				const initialAccounts = registry.select( STORE_NAME ).getAccounts();

				expect( initialAccounts ).toEqual( undefined );
				await subscribeUntil( registry,
					() => (
						registry.select( STORE_NAME ).getAccounts() !== undefined
					),
				);

				const accounts = registry.select( STORE_NAME ).getAccounts();
				expect( fetch ).toHaveBeenCalledTimes( 1 );

				// Properties and profiles should also have been received by
				// this action.
				muteConsole( 'error', 2 );
				const properties = registry.select( STORE_NAME ).getProperties( accountID );
				const profiles = registry.select( STORE_NAME ).getProfiles( accountID, propertyID );

				expect( accounts ).toEqual( fixtures.accountsPropertiesProfiles.accounts );
				expect( properties ).toEqual( fixtures.accountsPropertiesProfiles.properties );
				expect( profiles ).toEqual( fixtures.accountsPropertiesProfiles.profiles );
			} );

			it( 'does not make a network request if accounts are already present', async () => {
				registry.dispatch( STORE_NAME ).setSettings( {} );
				registry.dispatch( STORE_NAME ).receiveAccounts( fixtures.accountsPropertiesProfiles.accounts );

				const accounts = registry.select( STORE_NAME ).getAccounts();

				await subscribeUntil( registry, () => registry
					.select( STORE_NAME )
					.hasFinishedResolution( 'getAccounts' )
				);

				expect( accounts ).toEqual( fixtures.accountsPropertiesProfiles.accounts );
				expect( fetch ).not.toHaveBeenCalled();
			} );

			it( 'does not make a network request if accounts exist but are empty (this is a valid state)', async () => {
				registry.dispatch( STORE_NAME ).setSettings( {} );
				registry.dispatch( STORE_NAME ).receiveAccounts( [] );

				const accounts = registry.select( STORE_NAME ).getAccounts();

				await subscribeUntil( registry, () => registry
					.select( STORE_NAME )
					.hasFinishedResolution( 'getAccounts' )
				);

				expect( accounts ).toEqual( [] );
				expect( fetch ).not.toHaveBeenCalled();
			} );

			it( 'dispatches an error if the request fails', async () => {
				const response = {
					code: 'internal_server_error',
					message: 'Internal server error',
					data: { status: 500 },
				};
				fetch
					.doMockIf(
						/^\/google-site-kit\/v1\/modules\/analytics\/data\/accounts-properties-profiles/
					)
					.mockResponse(
						JSON.stringify( response ),
						{ status: 500 }
					);

				registry.dispatch( STORE_NAME ).receiveExistingTag( null );
				registry.dispatch( STORE_NAME ).setSettings( {} );
				muteConsole( 'error' );
				registry.select( STORE_NAME ).getAccounts();
				await subscribeUntil( registry,
					// TODO: We may want a selector for this, but for now this is fine
					// because it's internal-only.
					() => store.getState().isFetchingAccountsPropertiesProfiles === false,
				);

				expect( fetch ).toHaveBeenCalledTimes( 1 );

				const accounts = registry.select( STORE_NAME ).getAccounts();
				expect( accounts ).toEqual( undefined );
			} );

			it( 'passes existing tag ID when fetching accounts', async () => {
				const existingPropertyID = 'UA-12345-1';

				registry.dispatch( STORE_NAME ).receiveExistingTag( existingPropertyID );
				registry.dispatch( STORE_NAME ).receiveTagPermission( {
					accountID: '12345',
					propertyID: existingPropertyID,
					permission: true,
				} );
				registry.dispatch( STORE_NAME ).setSettings( {} );

				fetch
					.doMockOnceIf(
						/^\/google-site-kit\/v1\/modules\/analytics\/data\/accounts-properties-profiles/
					)
					.mockResponseOnce(
						JSON.stringify( fixtures.accountsPropertiesProfiles ),
						{ status: 200 }
					);

				registry.select( STORE_NAME ).getAccounts();

				await subscribeUntil( registry,
					() => registry.select( STORE_NAME ).getAccounts() !== undefined ||
					registry.select( STORE_NAME ).getError()
				);

				// Ensure the proper parameters were sent.
				expect( fetch.mock.calls[ 0 ][ 0 ] ).toMatchQueryParameters(
					{
						existingPropertyID,
					}
				);
				expect( fetch ).toHaveBeenCalledTimes( 1 );
			} );

			it( 'sets account, property, and profile IDs in the store, if a matchedProperty is received and an account is not selected yet', async () => {
				const { accounts, properties, profiles, matchedProperty } = fixtures.accountsPropertiesProfiles;
				const matchedProfile = {
					...fixtures.profiles[ 0 ],
					id: '123456',
					webPropertyId: matchedProperty.id,
					accountId: matchedProperty.accountId,
				};
				const response = {
					accounts,
					properties,
					profiles: [
						matchedProfile,
						...profiles,
					],
					matchedProperty,
				};

				registry.dispatch( STORE_NAME ).setSettings( {} );
				registry.dispatch( STORE_NAME ).receiveExistingTag( null );

				fetch
					.doMockOnceIf(
						/^\/google-site-kit\/v1\/modules\/analytics\/data\/accounts-properties-profiles/
					)
					.mockResponseOnce(
						JSON.stringify( response ),
						{ status: 200 }
					);

				expect( store.getState().matchedProperty ).toBeFalsy();
				expect( registry.select( STORE_NAME ).getAccountID() ).toBeFalsy();
				expect( registry.select( STORE_NAME ).getPropertyID() ).toBeFalsy();
				expect( registry.select( STORE_NAME ).getInternalWebPropertyID() ).toBeFalsy();
				expect( registry.select( STORE_NAME ).getProfileID() ).toBeFalsy();

				registry.select( STORE_NAME ).getAccounts();

				await subscribeUntil( registry,
					() => (
						registry.select( STORE_NAME ).getAccounts() !== undefined
					),
				);

				expect( store.getState().matchedProperty ).toMatchObject( matchedProperty );
				expect( registry.select( STORE_NAME ).getAccountID() ).toBe( matchedProperty.accountId );
				expect( registry.select( STORE_NAME ).getPropertyID() ).toBe( matchedProperty.id );
				expect( registry.select( STORE_NAME ).getInternalWebPropertyID() ).toBe( matchedProperty.internalWebPropertyId );
				expect( registry.select( STORE_NAME ).getProfileID() ).toBe( matchedProfile.id );
			} );
		} );
	} );
} );
<|endoftext|>


<commit_before>/**
 * modules/analytics data store: accounts tests.
 *
 * Site Kit by Google, Copyright 2020 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * WordPress dependencies
 */
import apiFetch from '@wordpress/api-fetch';

/**
 * Internal dependencies
 */
import API from 'googlesitekit-api';
import { STORE_NAME } from './index';
import {
	createTestRegistry,
	muteConsole,
	subscribeUntil,
	unsubscribeFromAll,
} from 'tests/js/utils';
import * as fixtures from './__fixtures__';

describe( 'modules/analytics accounts', () => {
	let apiFetchSpy;
	let locationAssignSpy;
	let registry;
	let store;
	let redirect;

	const accountName = fixtures.createAccount.account.name;
	const propertyName = fixtures.createAccount.webproperty.name;
	const profileName = fixtures.createAccount.profile.name;
	const timezone = fixtures.createAccount.profile.timezone;

	beforeAll( () => {
		API.setUsingCache( false );
	} );

	beforeEach( () => {
		registry = createTestRegistry();
		store = registry.stores[ STORE_NAME ].store;
		apiFetchSpy = jest.spyOn( { apiFetch }, 'apiFetch' );
		locationAssignSpy = jest.spyOn( location, 'assign' );

		locationAssignSpy.mockImplementation( ( location ) => {
			redirect = location;
		} );
		redirect = '';
	} );

	afterAll( () => {
		API.setUsingCache( true );
	} );

	afterEach( () => {
		unsubscribeFromAll( registry );
		apiFetchSpy.mockRestore();
		locationAssignSpy.mockRestore();
	} );

	describe( 'actions', () => {
		describe( 'createAccount', () => {
			it( 'creates an account ticket and redirects to the Terms of Service', async () => {
				fetch
					.doMockIf(
						/^\/google-site-kit\/v1\/modules\/analytics\/data\/create-account-ticket/
					)
					.mockResponse(
						JSON.stringify( fixtures.createAccount ),
						{ status: 200 }
					);

				muteConsole( 'error' );

				registry.dispatch( STORE_NAME ).createAccount( { accountName, propertyName, profileName, timezone } );
				await subscribeUntil( registry,
					() => (
						registry.select( STORE_NAME ).isDoingCreateAccount() !== true
					),
				);

				// Ensure the proper body parameters were sent.
				expect( JSON.parse( fetch.mock.calls[ 0 ][ 1 ].body ).data ).toMatchObject(
					{ accountName, propertyName, profileName, timezone }
				);

				expect( redirect ).toEqual( `https://analytics.google.com/analytics/web/?provisioningSignup=false#management/TermsOfService/&api.accountTicketId=${ fixtures.createAccount.id }` );
			} );

			it( 'sets isDoingCreateAccount ', async () => {
				fetch
					.doMockIf(
						/^\/google-site-kit\/v1\/modules\/analytics\/data\/create-account-ticket/
					)
					.mockResponse(
						JSON.stringify( fixtures.createAccount ),
						{ status: 200 }
					);

				registry.dispatch( STORE_NAME ).fetchCreateAccount( { accountName, propertyName, profileName, timezone } );
				expect( registry.select( STORE_NAME ).isDoingCreateAccount() ).toEqual( true );
			} );

			it( 'dispatches an error if the request fails ', async () => {
				const response = {
					code: 'internal_server_error',
					message: 'Internal server error',
					data: { status: 500 },
				};

				fetch
					.doMockIf(
						/^\/google-site-kit\/v1\/modules\/analytics\/data\/create-account-ticket/
					)
					.mockResponse(
						JSON.stringify( response ),
						{ status: 500 }
					);

				muteConsole( 'error' );

				registry.dispatch( STORE_NAME ).createAccount( { accountName, propertyName, profileName, timezone } );

				await subscribeUntil( registry,
					() => (
						registry.select( STORE_NAME ).isDoingCreateAccount() === false
					),
				);

				expect( registry.select( STORE_NAME ).getError() ).toMatchObject( response );

				expect( redirect ).toEqual( '' );
			} );
		} );
	} );

	describe( 'selectors', () => {
		describe( 'getAccounts', () => {
			it( 'uses a resolver to make a network request', async () => {
				fetch
					.doMockOnceIf(
						/^\/google-site-kit\/v1\/modules\/analytics\/data\/accounts-properties-profiles/
					)
					.mockResponseOnce(
						JSON.stringify( fixtures.accountsPropertiesProfiles ),
						{ status: 200 }
					);

				const accountID = fixtures.accountsPropertiesProfiles.properties[ 0 ].accountId; // Capitalization rule exception: `accountId` is a property of an API returned value.
				const propertyID = fixtures.accountsPropertiesProfiles.profiles[ 0 ].webPropertyId; // Capitalization rule exception: `webPropertyId` is a property of an API returned value.

				const initialAccounts = registry.select( STORE_NAME ).getAccounts();
				// The connection info will be its initial value while the connection
				// info is fetched.
				expect( initialAccounts ).toEqual( undefined );
				await subscribeUntil( registry,
					() => (
						registry.select( STORE_NAME ).getAccounts() !== undefined
					),
				);

				const accounts = registry.select( STORE_NAME ).getAccounts();
				expect( fetch ).toHaveBeenCalledTimes( 1 );

				// Properties and profiles should also have been received by
				// this action.
				muteConsole( 'error', 2 );
				const properties = registry.select( STORE_NAME ).getProperties( accountID );
				const profiles = registry.select( STORE_NAME ).getProfiles( accountID, propertyID );

				expect( accounts ).toEqual( fixtures.accountsPropertiesProfiles.accounts );
				expect( properties ).toEqual( fixtures.accountsPropertiesProfiles.properties );
				expect( profiles ).toEqual( fixtures.accountsPropertiesProfiles.profiles );
			} );

			it( 'does not make a network request if accounts are already present', async () => {
				registry.dispatch( STORE_NAME ).receiveAccounts( fixtures.accountsPropertiesProfiles.accounts );

				const accounts = registry.select( STORE_NAME ).getAccounts();

				await subscribeUntil( registry, () => registry
					.select( STORE_NAME )
					.hasFinishedResolution( 'getAccounts' )
				);

				expect( accounts ).toEqual( fixtures.accountsPropertiesProfiles.accounts );
				expect( fetch ).not.toHaveBeenCalled();
			} );

			it( 'dispatches an error if the request fails', async () => {
				const response = {
					code: 'internal_server_error',
					message: 'Internal server error',
					data: { status: 500 },
				};
				fetch
					.doMockIf(
						/^\/google-site-kit\/v1\/modules\/analytics\/data\/accounts-properties-profiles/
					)
					.mockResponse(
						JSON.stringify( response ),
						{ status: 500 }
					);

				muteConsole( 'error' );
				registry.select( STORE_NAME ).getAccounts();
				await subscribeUntil( registry,
					// TODO: We may want a selector for this, but for now this is fine
					// because it's internal-only.
					() => store.getState().isFetchingAccountsPropertiesProfiles === false,
				);

				expect( fetch ).toHaveBeenCalledTimes( 1 );

				const accounts = registry.select( STORE_NAME ).getAccounts();
				expect( accounts ).toEqual( undefined );
			} );
		} );
	} );
} );
<commit_msg>Improve comparison clarity.<commit_after>/**
 * modules/analytics data store: accounts tests.
 *
 * Site Kit by Google, Copyright 2020 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * WordPress dependencies
 */
import apiFetch from '@wordpress/api-fetch';

/**
 * Internal dependencies
 */
import API from 'googlesitekit-api';
import { STORE_NAME } from './index';
import {
	createTestRegistry,
	muteConsole,
	subscribeUntil,
	unsubscribeFromAll,
} from 'tests/js/utils';
import * as fixtures from './__fixtures__';

describe( 'modules/analytics accounts', () => {
	let apiFetchSpy;
	let locationAssignSpy;
	let registry;
	let store;
	let redirect;

	const accountName = fixtures.createAccount.account.name;
	const propertyName = fixtures.createAccount.webproperty.name;
	const profileName = fixtures.createAccount.profile.name;
	const timezone = fixtures.createAccount.profile.timezone;

	beforeAll( () => {
		API.setUsingCache( false );
	} );

	beforeEach( () => {
		registry = createTestRegistry();
		store = registry.stores[ STORE_NAME ].store;
		apiFetchSpy = jest.spyOn( { apiFetch }, 'apiFetch' );
		locationAssignSpy = jest.spyOn( location, 'assign' );

		locationAssignSpy.mockImplementation( ( location ) => {
			redirect = location;
		} );
		redirect = '';
	} );

	afterAll( () => {
		API.setUsingCache( true );
	} );

	afterEach( () => {
		unsubscribeFromAll( registry );
		apiFetchSpy.mockRestore();
		locationAssignSpy.mockRestore();
	} );

	describe( 'actions', () => {
		describe( 'createAccount', () => {
			it( 'creates an account ticket and redirects to the Terms of Service', async () => {
				fetch
					.doMockIf(
						/^\/google-site-kit\/v1\/modules\/analytics\/data\/create-account-ticket/
					)
					.mockResponse(
						JSON.stringify( fixtures.createAccount ),
						{ status: 200 }
					);

				muteConsole( 'error' );

				registry.dispatch( STORE_NAME ).createAccount( { accountName, propertyName, profileName, timezone } );
				await subscribeUntil( registry,
					() => (
						registry.select( STORE_NAME ).isDoingCreateAccount() === false
					),
				);

				// Ensure the proper body parameters were sent.
				expect( JSON.parse( fetch.mock.calls[ 0 ][ 1 ].body ).data ).toMatchObject(
					{ accountName, propertyName, profileName, timezone }
				);

				expect( redirect ).toEqual( `https://analytics.google.com/analytics/web/?provisioningSignup=false#management/TermsOfService/&api.accountTicketId=${ fixtures.createAccount.id }` );
			} );

			it( 'sets isDoingCreateAccount ', async () => {
				fetch
					.doMockIf(
						/^\/google-site-kit\/v1\/modules\/analytics\/data\/create-account-ticket/
					)
					.mockResponse(
						JSON.stringify( fixtures.createAccount ),
						{ status: 200 }
					);

				registry.dispatch( STORE_NAME ).fetchCreateAccount( { accountName, propertyName, profileName, timezone } );
				expect( registry.select( STORE_NAME ).isDoingCreateAccount() ).toEqual( true );
			} );

			it( 'dispatches an error if the request fails ', async () => {
				const response = {
					code: 'internal_server_error',
					message: 'Internal server error',
					data: { status: 500 },
				};

				fetch
					.doMockIf(
						/^\/google-site-kit\/v1\/modules\/analytics\/data\/create-account-ticket/
					)
					.mockResponse(
						JSON.stringify( response ),
						{ status: 500 }
					);

				muteConsole( 'error' );

				registry.dispatch( STORE_NAME ).createAccount( { accountName, propertyName, profileName, timezone } );

				await subscribeUntil( registry,
					() => (
						registry.select( STORE_NAME ).isDoingCreateAccount() === false
					),
				);

				expect( registry.select( STORE_NAME ).getError() ).toMatchObject( response );

				expect( redirect ).toEqual( '' );
			} );
		} );
	} );

	describe( 'selectors', () => {
		describe( 'getAccounts', () => {
			it( 'uses a resolver to make a network request', async () => {
				fetch
					.doMockOnceIf(
						/^\/google-site-kit\/v1\/modules\/analytics\/data\/accounts-properties-profiles/
					)
					.mockResponseOnce(
						JSON.stringify( fixtures.accountsPropertiesProfiles ),
						{ status: 200 }
					);

				const accountID = fixtures.accountsPropertiesProfiles.properties[ 0 ].accountId; // Capitalization rule exception: `accountId` is a property of an API returned value.
				const propertyID = fixtures.accountsPropertiesProfiles.profiles[ 0 ].webPropertyId; // Capitalization rule exception: `webPropertyId` is a property of an API returned value.

				const initialAccounts = registry.select( STORE_NAME ).getAccounts();
				// The connection info will be its initial value while the connection
				// info is fetched.
				expect( initialAccounts ).toEqual( undefined );
				await subscribeUntil( registry,
					() => (
						registry.select( STORE_NAME ).getAccounts() !== undefined
					),
				);

				const accounts = registry.select( STORE_NAME ).getAccounts();
				expect( fetch ).toHaveBeenCalledTimes( 1 );

				// Properties and profiles should also have been received by
				// this action.
				muteConsole( 'error', 2 );
				const properties = registry.select( STORE_NAME ).getProperties( accountID );
				const profiles = registry.select( STORE_NAME ).getProfiles( accountID, propertyID );

				expect( accounts ).toEqual( fixtures.accountsPropertiesProfiles.accounts );
				expect( properties ).toEqual( fixtures.accountsPropertiesProfiles.properties );
				expect( profiles ).toEqual( fixtures.accountsPropertiesProfiles.profiles );
			} );

			it( 'does not make a network request if accounts are already present', async () => {
				registry.dispatch( STORE_NAME ).receiveAccounts( fixtures.accountsPropertiesProfiles.accounts );

				const accounts = registry.select( STORE_NAME ).getAccounts();

				await subscribeUntil( registry, () => registry
					.select( STORE_NAME )
					.hasFinishedResolution( 'getAccounts' )
				);

				expect( accounts ).toEqual( fixtures.accountsPropertiesProfiles.accounts );
				expect( fetch ).not.toHaveBeenCalled();
			} );

			it( 'dispatches an error if the request fails', async () => {
				const response = {
					code: 'internal_server_error',
					message: 'Internal server error',
					data: { status: 500 },
				};
				fetch
					.doMockIf(
						/^\/google-site-kit\/v1\/modules\/analytics\/data\/accounts-properties-profiles/
					)
					.mockResponse(
						JSON.stringify( response ),
						{ status: 500 }
					);

				muteConsole( 'error' );
				registry.select( STORE_NAME ).getAccounts();
				await subscribeUntil( registry,
					// TODO: We may want a selector for this, but for now this is fine
					// because it's internal-only.
					() => store.getState().isFetchingAccountsPropertiesProfiles === false,
				);

				expect( fetch ).toHaveBeenCalledTimes( 1 );

				const accounts = registry.select( STORE_NAME ).getAccounts();
				expect( accounts ).toEqual( undefined );
			} );
		} );
	} );
} );
<|endoftext|>


<commit_before>import { Component, EditorSession } from 'substance'
import { EditorPackage as TextureEditorPackage } from 'substance-texture'

import Engine from '../engine/NewEngine'
import DocumentEngineAdapter from '../document/DocumentEngineAdapter'

export default class ArticlePage extends Component {

  constructor(parent, props) {
    super(parent, props)

    const host = props.host
    this.functionManager = host.functionManager
    this.engine = new Engine(host)
  }

  getInitialState() {
    return {
      editorSession: null,
      article: null
    }
  }

  didMount() {
    this._initializeEditorSession(this.props.article)
  }

  getChildContext() {
    return {
      app: this,
      editorSession: this.state.editorSession,
      configurator: this.props.configurator
    }
  }

  render($$) {
    const editorSession = this.getEditorSession()
    let el = $$('div').addClass('sc-article-page')
    if (!editorSession) {
      el.text('Loading...')
    } else {
      el.append(
        $$(TextureEditorPackage.Editor, {editorSession}
      ).addClass('sc-document-editor'))
    }
    return el
  }

  getEditorSession() {
    return this.state.editorSession
  }

  _initializeEditorSession(article) {
    let editorSession = new EditorSession(article, {
      configurator: this.props.configurator,
      context: {
        app: this,
        host: this.props.host
      }
    })

    let engineAdapter = new DocumentEngineAdapter(editorSession)
    engineAdapter.connect(this.engine)
    this.engine.editorSession = editorSession

    this.extendState({
      editorSession,
      article
    })
  }
}

<commit_msg>Fix article function usage<commit_after>import { Component, EditorSession } from 'substance'
import { EditorPackage as TextureEditorPackage } from 'substance-texture'

import Engine from '../engine/NewEngine'
import DocumentEngineAdapter from '../document/DocumentEngineAdapter'

export default class ArticlePage extends Component {

  constructor(parent, props) {
    super(parent, props)

    const host = props.host
    this.functionManager = host.functionManager
    this.engine = new Engine(host)
  }

  getInitialState() {
    return {
      editorSession: null,
      article: null
    }
  }

  didMount() {
    this._initializeEditorSession(this.props.article)
  }

  getChildContext() {
    return {
      app: this,
      editorSession: this.state.editorSession,
      configurator: this.props.configurator,
      functionManager: this.functionManager,
      cellEngine: this.engine
    }
  }

  render($$) {
    const editorSession = this.getEditorSession()
    let el = $$('div').addClass('sc-article-page')
    if (!editorSession) {
      el.text('Loading...')
    } else {
      el.append(
        $$(TextureEditorPackage.Editor, {editorSession}
      ).addClass('sc-document-editor'))
    }
    return el
  }

  getEditorSession() {
    return this.state.editorSession
  }

  _initializeEditorSession(article) {
    let editorSession = new EditorSession(article, {
      configurator: this.props.configurator,
      context: {
        app: this,
        host: this.props.host
      }
    })

    let engineAdapter = new DocumentEngineAdapter(editorSession)
    engineAdapter.connect(this.engine)
    this.engine.editorSession = editorSession

    this.extendState({
      editorSession,
      article
    })
  }
}

<|endoftext|>


<commit_before>var mumuki = mumuki || {};

(function (mumuki) {
  function ResultsBox(submissionsResults) {
    this.submissionsResultsArea = submissionsResults;
    this.processingTemplate = $('#processing-template');
    this.submissionsErrorTemplate = $(".submission-result-error");
  }

  ResultsBox.prototype = {
    waiting: function () {
      this.submissionsResultsArea.html(this.processingTemplate.html());
      this.submissionsErrorTemplate.hide();
    },
    success: function (data) {
      this.submissionsResultsArea.html(data);
    },
    error: function () {
      this.submissionsResultsArea.html('');
      this.submissionsErrorTemplate.show();
    },
    done: function () {
      mumuki.pin.scroll();
    }
  };

  function SubmitButton() {
    this.submitButton = $('.btn-submit');
    this.submissionControls = $('.submission_control');
  }

  SubmitButton.prototype = {
    disable: function () {
      document.prevSubmitState = this.submitButton.html();
      this.submitButton.html(' ' + this.submitButton.attr('data-waiting'));
      this.submissionControls.attr('disabled', 'disabled');
    },
    enable: function () {
      this.submitButton.html(document.prevSubmitState);
      this.submissionControls.removeAttr('disabled');
    }
  };

  mumuki.load(function () {
    var submissionsResults = $('.submission-results');
    if (!submissionsResults) return;

    var resultsBox = new ResultsBox(submissionsResults);
    var submitButton = new SubmitButton();

    $('form.new_solution').on('ajax:beforeSend', function (event) {
      submitButton.disable();
      resultsBox.waiting();
    }).on('ajax:complete', function (event) {
      $(document).renderMuComponents();
      resultsBox.done();
      $('#messages-tab').removeClass('hidden');
    }).on('ajax:success', function (event) {
      var data = event.detail[0].body.outerHTML;
      submitButton.enable();
      resultsBox.success(data);
    }).on('ajax:error', function (event) {
      resultsBox.error(submitButton);
      animateTimeoutError(submitButton);
    });
  });

  function animateTimeoutError(submissionController) {
    var image = $('#submission-result-error-animation')[0];
    image.src = mumuki.errors.error_timeout_1;
    setTimeout(function () {
      image.src = mumuki.errors.error_timeout_2;
      setTimeout(function () {
        image.src = mumuki.errors.error_timeout_3;
        submissionController.enable();
      }, 4000);
    }, 10333);
  }

})(mumuki);
<commit_msg>setting send text to button<commit_after>var mumuki = mumuki || {};

(function (mumuki) {
  function ResultsBox(submissionsResults) {
    this.submissionsResultsArea = submissionsResults;
    this.processingTemplate = $('#processing-template');
    this.submissionsErrorTemplate = $(".submission-result-error");
  }

  ResultsBox.prototype = {
    waiting: function () {
      this.submissionsResultsArea.html(this.processingTemplate.html());
      this.submissionsErrorTemplate.hide();
    },
    success: function (data) {
      this.submissionsResultsArea.html(data);
    },
    error: function () {
      this.submissionsResultsArea.html('');
      this.submissionsErrorTemplate.show();
    },
    done: function () {
      mumuki.pin.scroll();
    }
  };

  function SubmitButton() {
    this.submitButton = $('.btn-submit');
    this.submissionControls = $('.submission_control');
  }

  SubmitButton.prototype = {
    disable: function () {
      document.prevSubmitState = this.submitButton.html();
      this.submitButton.html(' ' + this.submitButton.attr('data-waiting'));
      this.submissionControls.attr('disabled', 'disabled');
    },
    setSendText: function () {
      this.submitButton.html(document.prevSubmitState);
    },
    enable: function () {
      this.submitButton.html(document.prevSubmitState);
      this.submissionControls.removeAttr('disabled');
    }
  };

  mumuki.load(function () {
    var submissionsResults = $('.submission-results');
    if (!submissionsResults) return;

    var resultsBox = new ResultsBox(submissionsResults);
    var submitButton = new SubmitButton();

    $('form.new_solution').on('ajax:beforeSend', function (event) {
      submitButton.disable();
      resultsBox.waiting();
    }).on('ajax:complete', function (event) {
      $(document).renderMuComponents();
      resultsBox.done();
      $('#messages-tab').removeClass('hidden');
    }).on('ajax:success', function (event) {
      var data = event.detail[0].body.outerHTML;
      submitButton.enable();
      resultsBox.success(data);
    }).on('ajax:error', function (event) {
      resultsBox.error(submitButton);
      animateTimeoutError(submitButton);
    });
  });

  function animateTimeoutError(submitButton) {
    var image = $('#submission-result-error-animation')[0];
    image.src = mumuki.errors.error_timeout_1;
    submitButton.setSendText();
    setTimeout(function () {
      image.src = mumuki.errors.error_timeout_2;
      setTimeout(function () {
        image.src = mumuki.errors.error_timeout_3;
        submitButton.enable();
      }, 4000);
    }, 10333);
  }

})(mumuki);
<|endoftext|>