<commit_before>// Dependencies
var Ul = require("ul")
  , Abs = require("abs")
  , ReadJson = require("r-json")
  , WriteJson = require("w-json")
  , Moment = require("moment")
  , Gry = require("gry")
  , IsThere = require("is-there")
  , CliPie = require("cli-pie")
  , CliGhCal = require("cli-gh-cal")
  , GitLogParser = require("gitlog-parser").parse
  , ChildProcess = require("child_process")
  , Deffy = require("deffy")
  , Typpy = require("typpy")
  , Exec = ChildProcess.exec
  , Spawn = ChildProcess.spawn
  ;

// Constants
const DATE_FORMAT = "MMM D, YYYY"
    , DEFAULT_STORE = Abs("~/.git-stats")
    , CONFIG_PATH = Abs("~/.git-stats-config.json")
    , DEFAULT_CONFIG = {}
    ;

function GitStats(config, data) {
    var self = this;
    this.config_path = null;

    if (typeof config === "object") {
        self.config = config;
    } else {
        this.config_path = Deffy(confPath, CONFIG_PATH)
        self.config = this.getConfig();
    }

    this.path = Deffy(confPath, DEFAULT_STORE);
}

GitStats.prototype.getConfig = function () {
    try {
        return ReadJson(this.config_path);
    } catch (err) {
        if (err.code === "ENOENT") {
            return DEFAULT_CONFIG;
        }
        throw err;
    }
};

/**
 * record
 * Records a new commit.
 *
 * @name record
 * @function
 * @param {Object} data The commit data containing:
 *
 *  - `date` (String|Date): The date object or a string in a format that can be parsed.
 *  - `url` (String): The repository remote url.
 *  - `hash` (String): The commit hash.
 *
 * @param {Function} callback The callback function.
 * @return {GitStats} The `GitStats` object.
 */
GitStats.prototype.record = function (data, callback) {

    var self = this;

    // Validate data
    callback = callback || function (err) { if (err) throw err; };
    data = Object(data);

    if (typeof data.date === "string") {
        data.date = new Moment(new Date(data.date));
    }

    if (!/^moment|date$/.test(Typpy(data.date))) {
        callback(new Error("The date field should be a string or a date object."));
        return GitStats;
    }

    if (typeof data.hash !== "string" || !data.hash) {
        callback(new Error("Invalid hash."));
        return GitStats;
    }

    if (typeof data.url !== "string" || !data.url) {
        callback(new Error("Invalid url field. This commit is not recorded into the git-stats history since you haven't added the remote url. You can import the previous commits using the git-stats-importer tool."));
        return GitStats;
    }

    // Get stats
    self.get(function (err, stats) {
        stats = stats || {};

        var day = data.date.format(DATE_FORMAT)
          , today = stats[day] = Object(stats[day])
          , repo = today[data.url] = Object(today[data.url])
          ;

        repo[data.hash] = { date: data.date };

        self.save(stats, callback);
    });

    return self;
};

/**
 * get
 * Gets the git stats.
 *
 * @name get
 * @function
 * @param {Function} callback The callback function.
 * @return {GitStats} The `GitStats` object.
 */
GitStats.prototype.get = function (callback) {
    var self = this;
    ReadJson(self.path, function (err, data) {

        if (err && err.code === "ENOENT") {
           return self.save({}, function (err) {
               callback(err, {});
           });
        }

        if (err) { return callback(err); }
        callback(null, data);
    });
    return self;
};

/**
 * save
 * Saves the provided stats.
 *
 * @name save
 * @function
 * @param {Object} stats The stats to be saved.
 * @param {Function} callback The callback function.
 * @return {GitStats} The `GitStats` object.
 */
GitStats.prototype.save = function (stats, callback) {
    WriteJson(this.path, stats, callback);
    return this;
};

/**
 * iterateDays
 * Iterate through the days, calling the callback function on each day.
 *
 * @name iterateDays
 * @function
 * @param {Object} data An object containing the following fields:
 *
 *  - `start` (Moment): A `Moment` date object representing the start date (default: *an year ago*).
 *  - `end` (Moment): A `Moment` date object representing the end date (default: *now*).
 *  - `format` (String): The format of the date (default: `"MMM D, YYYY"`).
 *
 * @param {Function} callback The callback function called with the current day formatted (type: string) and the `Moment` date object.
 * @return {GitStats} The `GitStats` object.
 */
GitStats.prototype.iterateDays = function (data, callback) {

    if (typeof data === "function") {
        callback = data;
        data = undefined;
    }

    // Merge the defaults
    data.end = data.end || Moment();
    data.start = data.start || Moment().subtract(1, "years");
    data.format = data.format || DATE_FORMAT;

    var start = new Moment(data.start.format(DATE_FORMAT), DATE_FORMAT)
      , end = new Moment(data.end.format(DATE_FORMAT), DATE_FORMAT)
      , tomrrow = Moment(end.format(DATE_FORMAT), DATE_FORMAT).add(1, "days")
      , endStr = tomrrow.format(DATE_FORMAT)
      , cDay = null
      ;

    while (start.format(DATE_FORMAT) !== endStr) {
        cDay = start.format(data.format);
        callback(cDay, start);
        start.add(1, "days");
    }

    return this;
};

/**
 * graph
 * Creates an object with the stats on the provided period (default: *last year*).
 *
 * @name graph
 * @function
 * @param {Object} data The object passed to the `iterateDays` method.
 * @param {Function} callback The callback function.
 * @return {GitStats} The `GitStats` object.
 */
GitStats.prototype.graph = function (data, callback) {

    if (typeof data === "function") {
        callback = data;
        data = undefined;
    }

    var self = this;

    // Get commits
    self.get(function (err, stats) {
        if (err) { return callback(err); }

        var cDayObj = null
          , year = {}
          ;

        // Iterate days
        self.iterateDays(data, function (cDay) {
            cDayObj = year[cDay] = {
                _: stats[cDay] || {}
              , c: 0
            };

            Object.keys(cDayObj._).forEach(function (c) {
                cDayObj.c += Object.keys(cDayObj._[c]).length;
            });
        });

        callback(null, year);
    });

    return self;
};

/**
 * calendar
 * Creates the calendar data for the provided period (default: *last year*).
 *
 * @name calendar
 * @function
 * @param {Object} data The object passed to the `graph` method.
 * @param {Function} callback The callback function.
 * @return {GitStats} The `GitStats` object.
 */
GitStats.prototype.calendar = function (data, callback) {

    var self = this;

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

        var cal = { total: 0, days: {}, cStreak: 0, lStreak: 0, max: 0 }
          , cDay = null
          , days = Object.keys(graph)
          , levels = null
          , cLevel = 0
          ;

        days.forEach(function (c) {
            cDay = graph[c];
            cal.total += cDay.c;
            if (cDay.c > cal.max) {
                cal.max = cDay.c;
            }

            if (cDay.c > 0) {
                if (++cal.cStreak > cal.lStreak) {
                    cal.lStreak = cal.cStreak;
                }
            } else {
                cal.cStreak = 0;
            }
        });

        levels = cal.max / (LEVELS.length * 2);
        days.forEach(function (c) {
            cDay = graph[c];
            cal.days[c] = {
                c: cDay.c
              , level: !levels
              ? 0 : (cLevel = Math.round(cDay.c / levels)) >= 4
              ? 4 : !cLevel && cDay.c > 0 ? 1 : cLevel
            };
        });

        callback(null, cal);
    });
    return self;
};

/**
 * ansiCalendar
 * Creates the ANSI contributions calendar.
 *
 * @name ansiCalendar
 * @function
 * @param {Object} data The object passed to the `calendar` method.
 * @param {Function} callback The callback function.
 * @return {GitStats} The `GitStats` object.
 */
GitStats.prototype.ansiCalendar = function (data, callback) {

    if (typeof data === "function") {
        callback = data;
        data = undefined;
    }

    var self = this;

    self.graph(data, function (err, graph) {
        var cal = [];

        self.iterateDays(data, function (cDay) {
            cDayObj = graph[cDay];
            if (!cDayObj) { return; }
            cal.push([cDay, cDayObj.c]);
        });

        callback(null, CliGhCal(cal, {
            theme: data.theme
          , start: data.start
          , end: data.end
          , firstDay: data.firstDay
        }));
    });

    return self;
};

GitStats.prototype.authors = function (options, callback) {
    var repo = new Gry(options.repo);
    repo.exec("shortlog -s -n --all", function (err, stdout) {
        if (err) { return callback(err); }
        lines = stdout.split("\n");
        pieData = stdout.split("\n").map(function (c) {
            var splits = c.split("\t").map(function (cc) {
                return cc.trim();
            });
            return {
                value: parseInt(splits[0])
              , label: splits[1]
            };
        });
        callback(null, pieData);
    });
    return this;
};

GitStats.prototype.authorsPie = function (options, callback) {
    if (typeof options === "string") {
        options = {
            repo: options
        };
    }

    options = Ul.merge(options, {
        radius: process.stdout.rows / 2 || 20
    });

    if (!IsThere(options.repo)) {
        return callback(new Error("The repository folder doesn't exist."));
    }

    var self = this
      , repo = new Gry(options.repo)
      , pie = null
      , pieData = []
      ;

    self.authors(options, function (err, authors) {
        if (err) { return callback(err); }
        if (authors.length > 50) {
            var others = {
                value: authors.slice(50).reduce(function (a, b) {
                    return a + b.value;
                }, 0)
              , label: "Others"
            };
            authors = authors.slice(0, 50);
            authors.push(others);
        }

        pie = new CliPie(options.radius, authors, {
            legend: true
          , flat: true
          , no_ansi: options.no_ansi
        });

        callback(null, pie.toString());
    });

    return self;
};

GitStats.prototype.globalActivity = function (options, callback) {

    if (typeof options === "string") {
        options = {
            repo: options
        };
    }

    options.repo = Abs(options.repo);

    if (!IsThere(options.repo)) {
        return callback(new Error("The repository folder doesn't exist."));
    }

    var commits = {}
      , today = null
      , cal = []
      ;

    GitLogParser(Spawn("git", ["log", "--since", options.start.format(DATE_FORMAT), "--until", options.end.format(DATE_FORMAT)], { cwd: options.repo }).stdout).on("commit", function(commit) {
        today = Moment(commit.date).format(DATE_FORMAT);
        commits[today] = commits[today] || 0;
        ++commits[today];
    }).on("error", function (err) {
        callback(err);
    }).on("finish", function () {
        Object.keys(commits).forEach(function (c) {
            cal.push([c, commits[c]])
        });
        callback(null, CliGhCal(cal, {
            theme: options.theme
          , start: options.start
          , end: options.end
        }));
    });

    return this;
};

module.exports = GitStats;
<commit_msg>Fixed the variables<commit_after>// Dependencies
var Ul = require("ul")
  , Abs = require("abs")
  , ReadJson = require("r-json")
  , WriteJson = require("w-json")
  , Moment = require("moment")
  , Gry = require("gry")
  , IsThere = require("is-there")
  , CliPie = require("cli-pie")
  , CliGhCal = require("cli-gh-cal")
  , GitLogParser = require("gitlog-parser").parse
  , ChildProcess = require("child_process")
  , Deffy = require("deffy")
  , Typpy = require("typpy")
  , Exec = ChildProcess.exec
  , Spawn = ChildProcess.spawn
  ;

// Constants
const DATE_FORMAT = "MMM D, YYYY"
    , DEFAULT_STORE = Abs("~/.git-stats")
    , CONFIG_PATH = Abs("~/.git-stats-config.json")
    , DEFAULT_CONFIG = {}
    ;

function GitStats(config, data) {
    var self = this;
    this.config_path = null;

    if (typeof config === "object") {
        self.config = config;
    } else {
        this.config_path = Deffy(config, CONFIG_PATH)
        self.config = this.getConfig();
    }

    this.path = Deffy(data, DEFAULT_STORE);
}

GitStats.prototype.getConfig = function () {
    try {
        return ReadJson(this.config_path);
    } catch (err) {
        if (err.code === "ENOENT") {
            return DEFAULT_CONFIG;
        }
        throw err;
    }
};

/**
 * record
 * Records a new commit.
 *
 * @name record
 * @function
 * @param {Object} data The commit data containing:
 *
 *  - `date` (String|Date): The date object or a string in a format that can be parsed.
 *  - `url` (String): The repository remote url.
 *  - `hash` (String): The commit hash.
 *
 * @param {Function} callback The callback function.
 * @return {GitStats} The `GitStats` object.
 */
GitStats.prototype.record = function (data, callback) {

    var self = this;

    // Validate data
    callback = callback || function (err) { if (err) throw err; };
    data = Object(data);

    if (typeof data.date === "string") {
        data.date = new Moment(new Date(data.date));
    }

    if (!/^moment|date$/.test(Typpy(data.date))) {
        callback(new Error("The date field should be a string or a date object."));
        return GitStats;
    }

    if (typeof data.hash !== "string" || !data.hash) {
        callback(new Error("Invalid hash."));
        return GitStats;
    }

    if (typeof data.url !== "string" || !data.url) {
        callback(new Error("Invalid url field. This commit is not recorded into the git-stats history since you haven't added the remote url. You can import the previous commits using the git-stats-importer tool."));
        return GitStats;
    }

    // Get stats
    self.get(function (err, stats) {
        stats = stats || {};

        var day = data.date.format(DATE_FORMAT)
          , today = stats[day] = Object(stats[day])
          , repo = today[data.url] = Object(today[data.url])
          ;

        repo[data.hash] = { date: data.date };

        self.save(stats, callback);
    });

    return self;
};

/**
 * get
 * Gets the git stats.
 *
 * @name get
 * @function
 * @param {Function} callback The callback function.
 * @return {GitStats} The `GitStats` object.
 */
GitStats.prototype.get = function (callback) {
    var self = this;
    ReadJson(self.path, function (err, data) {

        if (err && err.code === "ENOENT") {
           return self.save({}, function (err) {
               callback(err, {});
           });
        }

        if (err) { return callback(err); }
        callback(null, data);
    });
    return self;
};

/**
 * save
 * Saves the provided stats.
 *
 * @name save
 * @function
 * @param {Object} stats The stats to be saved.
 * @param {Function} callback The callback function.
 * @return {GitStats} The `GitStats` object.
 */
GitStats.prototype.save = function (stats, callback) {
    WriteJson(this.path, stats, callback);
    return this;
};

/**
 * iterateDays
 * Iterate through the days, calling the callback function on each day.
 *
 * @name iterateDays
 * @function
 * @param {Object} data An object containing the following fields:
 *
 *  - `start` (Moment): A `Moment` date object representing the start date (default: *an year ago*).
 *  - `end` (Moment): A `Moment` date object representing the end date (default: *now*).
 *  - `format` (String): The format of the date (default: `"MMM D, YYYY"`).
 *
 * @param {Function} callback The callback function called with the current day formatted (type: string) and the `Moment` date object.
 * @return {GitStats} The `GitStats` object.
 */
GitStats.prototype.iterateDays = function (data, callback) {

    if (typeof data === "function") {
        callback = data;
        data = undefined;
    }

    // Merge the defaults
    data.end = data.end || Moment();
    data.start = data.start || Moment().subtract(1, "years");
    data.format = data.format || DATE_FORMAT;

    var start = new Moment(data.start.format(DATE_FORMAT), DATE_FORMAT)
      , end = new Moment(data.end.format(DATE_FORMAT), DATE_FORMAT)
      , tomrrow = Moment(end.format(DATE_FORMAT), DATE_FORMAT).add(1, "days")
      , endStr = tomrrow.format(DATE_FORMAT)
      , cDay = null
      ;

    while (start.format(DATE_FORMAT) !== endStr) {
        cDay = start.format(data.format);
        callback(cDay, start);
        start.add(1, "days");
    }

    return this;
};

/**
 * graph
 * Creates an object with the stats on the provided period (default: *last year*).
 *
 * @name graph
 * @function
 * @param {Object} data The object passed to the `iterateDays` method.
 * @param {Function} callback The callback function.
 * @return {GitStats} The `GitStats` object.
 */
GitStats.prototype.graph = function (data, callback) {

    if (typeof data === "function") {
        callback = data;
        data = undefined;
    }

    var self = this;

    // Get commits
    self.get(function (err, stats) {
        if (err) { return callback(err); }

        var cDayObj = null
          , year = {}
          ;

        // Iterate days
        self.iterateDays(data, function (cDay) {
            cDayObj = year[cDay] = {
                _: stats[cDay] || {}
              , c: 0
            };

            Object.keys(cDayObj._).forEach(function (c) {
                cDayObj.c += Object.keys(cDayObj._[c]).length;
            });
        });

        callback(null, year);
    });

    return self;
};

/**
 * calendar
 * Creates the calendar data for the provided period (default: *last year*).
 *
 * @name calendar
 * @function
 * @param {Object} data The object passed to the `graph` method.
 * @param {Function} callback The callback function.
 * @return {GitStats} The `GitStats` object.
 */
GitStats.prototype.calendar = function (data, callback) {

    var self = this;

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

        var cal = { total: 0, days: {}, cStreak: 0, lStreak: 0, max: 0 }
          , cDay = null
          , days = Object.keys(graph)
          , levels = null
          , cLevel = 0
          ;

        days.forEach(function (c) {
            cDay = graph[c];
            cal.total += cDay.c;
            if (cDay.c > cal.max) {
                cal.max = cDay.c;
            }

            if (cDay.c > 0) {
                if (++cal.cStreak > cal.lStreak) {
                    cal.lStreak = cal.cStreak;
                }
            } else {
                cal.cStreak = 0;
            }
        });

        levels = cal.max / (LEVELS.length * 2);
        days.forEach(function (c) {
            cDay = graph[c];
            cal.days[c] = {
                c: cDay.c
              , level: !levels
              ? 0 : (cLevel = Math.round(cDay.c / levels)) >= 4
              ? 4 : !cLevel && cDay.c > 0 ? 1 : cLevel
            };
        });

        callback(null, cal);
    });
    return self;
};

/**
 * ansiCalendar
 * Creates the ANSI contributions calendar.
 *
 * @name ansiCalendar
 * @function
 * @param {Object} data The object passed to the `calendar` method.
 * @param {Function} callback The callback function.
 * @return {GitStats} The `GitStats` object.
 */
GitStats.prototype.ansiCalendar = function (data, callback) {

    if (typeof data === "function") {
        callback = data;
        data = undefined;
    }

    var self = this;

    self.graph(data, function (err, graph) {
        var cal = [];

        self.iterateDays(data, function (cDay) {
            cDayObj = graph[cDay];
            if (!cDayObj) { return; }
            cal.push([cDay, cDayObj.c]);
        });

        callback(null, CliGhCal(cal, {
            theme: data.theme
          , start: data.start
          , end: data.end
          , firstDay: data.firstDay
        }));
    });

    return self;
};

GitStats.prototype.authors = function (options, callback) {
    var repo = new Gry(options.repo);
    repo.exec("shortlog -s -n --all", function (err, stdout) {
        if (err) { return callback(err); }
        lines = stdout.split("\n");
        pieData = stdout.split("\n").map(function (c) {
            var splits = c.split("\t").map(function (cc) {
                return cc.trim();
            });
            return {
                value: parseInt(splits[0])
              , label: splits[1]
            };
        });
        callback(null, pieData);
    });
    return this;
};

GitStats.prototype.authorsPie = function (options, callback) {
    if (typeof options === "string") {
        options = {
            repo: options
        };
    }

    options = Ul.merge(options, {
        radius: process.stdout.rows / 2 || 20
    });

    if (!IsThere(options.repo)) {
        return callback(new Error("The repository folder doesn't exist."));
    }

    var self = this
      , repo = new Gry(options.repo)
      , pie = null
      , pieData = []
      ;

    self.authors(options, function (err, authors) {
        if (err) { return callback(err); }
        if (authors.length > 50) {
            var others = {
                value: authors.slice(50).reduce(function (a, b) {
                    return a + b.value;
                }, 0)
              , label: "Others"
            };
            authors = authors.slice(0, 50);
            authors.push(others);
        }

        pie = new CliPie(options.radius, authors, {
            legend: true
          , flat: true
          , no_ansi: options.no_ansi
        });

        callback(null, pie.toString());
    });

    return self;
};

GitStats.prototype.globalActivity = function (options, callback) {

    if (typeof options === "string") {
        options = {
            repo: options
        };
    }

    options.repo = Abs(options.repo);

    if (!IsThere(options.repo)) {
        return callback(new Error("The repository folder doesn't exist."));
    }

    var commits = {}
      , today = null
      , cal = []
      ;

    GitLogParser(Spawn("git", ["log", "--since", options.start.format(DATE_FORMAT), "--until", options.end.format(DATE_FORMAT)], { cwd: options.repo }).stdout).on("commit", function(commit) {
        today = Moment(commit.date).format(DATE_FORMAT);
        commits[today] = commits[today] || 0;
        ++commits[today];
    }).on("error", function (err) {
        callback(err);
    }).on("finish", function () {
        Object.keys(commits).forEach(function (c) {
            cal.push([c, commits[c]])
        });
        callback(null, CliGhCal(cal, {
            theme: options.theme
          , start: options.start
          , end: options.end
        }));
    });

    return this;
};

module.exports = GitStats;
<|endoftext|>


<commit_before>'use strict';

import $ from 'jquery';
import { Plugin } from './foundation.core.plugin';
import { onLoad } from './foundation.core.utils';
import { Keyboard } from './foundation.util.keyboard';
import { onImagesLoaded } from './foundation.util.imageLoader';
/**
 * Tabs module.
 * @module foundation.tabs
 * @requires foundation.util.keyboard
 * @requires foundation.util.imageLoader if tabs contain images
 */

class Tabs extends Plugin {
  /**
   * Creates a new instance of tabs.
   * @class
   * @name Tabs
   * @fires Tabs#init
   * @param {jQuery} element - jQuery object to make into tabs.
   * @param {Object} options - Overrides to the default plugin settings.
   */
  _setup(element, options) {
    this.$element = element;
    this.options = $.extend({}, Tabs.defaults, this.$element.data(), options);
    this.className = 'Tabs'; // ie9 back compat

    this._init();
    Keyboard.register('Tabs', {
      'ENTER': 'open',
      'SPACE': 'open',
      'ARROW_RIGHT': 'next',
      'ARROW_UP': 'previous',
      'ARROW_DOWN': 'next',
      'ARROW_LEFT': 'previous'
      // 'TAB': 'next',
      // 'SHIFT_TAB': 'previous'
    });
  }

  /**
   * Initializes the tabs by showing and focusing (if autoFocus=true) the preset active tab.
   * @private
   */
  _init() {
    var _this = this;

    this.$element.attr({'role': 'tablist'});
    this.$tabTitles = this.$element.find(`.${this.options.linkClass}`);
    this.$tabContent = $(`[data-tabs-content="${this.$element[0].id}"]`);

    this.$tabTitles.each(function(){
      var $elem = $(this),
          $link = $elem.find('a'),
          isActive = $elem.hasClass(`${_this.options.linkActiveClass}`),
          hash = $link.attr('data-tabs-target') || $link[0].hash.slice(1),
          linkId = $link[0].id ? $link[0].id : `${hash}-label`,
          $tabContent = $(`#${hash}`);

      $elem.attr({'role': 'presentation'});

      $link.attr({
        'role': 'tab',
        'aria-controls': hash,
        'aria-selected': isActive,
        'id': linkId,
        'tabindex': isActive ? '0' : '-1'
      });

      $tabContent.attr({
        'role': 'tabpanel',
        'aria-labelledby': linkId
      });

      // Save up the initial hash to return to it later when going back in history
      if (isActive) {
        _this._initialAnchor = `#${hash}`;
      }

      if(!isActive) {
        $tabContent.attr('aria-hidden', 'true');
      }

      if(isActive && _this.options.autoFocus){
        _this.onLoadListener = onLoad($(window), function() {
          $('html, body').animate({ scrollTop: $elem.offset().top }, _this.options.deepLinkSmudgeDelay, () => {
            $link.focus();
          });
        });
      }
    });

    if(this.options.matchHeight) {
      var $images = this.$tabContent.find('img');

      if ($images.length) {
        onImagesLoaded($images, this._setHeight.bind(this));
      } else {
        this._setHeight();
      }
    }

     //current context-bound function to open tabs on page load or history hashchange
    this._checkDeepLink = () => {
      var anchor = window.location.hash;

      // if there is no anchor, return to the initial tab
      if (!anchor.length && this._initialAnchor) {
        anchor = this._initialAnchor;
      }

      //need a hash and a relevant anchor in this tabset
      if (anchor.length) {
        var anchorNoHash = (anchor.indexOf('#') >= 0 ? anchor.slice(1) : anchor);
        var $link = this.$element.find(`[href$="${anchor}"],[data-tabs-target="${anchorNoHash}"]`).first();
        if ($link.length) {
          this.selectTab($(anchor), true);

          //roll up a little to show the titles
          if (this.options.deepLinkSmudge) {
            var offset = this.$element.offset();
            $('html, body').animate({ scrollTop: offset.top }, this.options.deepLinkSmudgeDelay);
          }

          /**
            * Fires when the zplugin has deeplinked at pageload
            * @event Tabs#deeplink
            */
           this.$element.trigger('deeplink.zf.tabs', [$link, $(anchor)]);
         }
       }
     }

    //use browser to open a tab, if it exists in this tabset
    if (this.options.deepLink) {
      this._checkDeepLink();
    }

    this._events();
  }

  /**
   * Adds event handlers for items within the tabs.
   * @private
   */
  _events() {
    this._addKeyHandler();
    this._addClickHandler();
    this._setHeightMqHandler = null;

    if (this.options.matchHeight) {
      this._setHeightMqHandler = this._setHeight.bind(this);

      $(window).on('changed.zf.mediaquery', this._setHeightMqHandler);
    }

    if(this.options.deepLink) {
      $(window).on('hashchange', this._checkDeepLink);
    }
  }

  /**
   * Adds click handlers for items within the tabs.
   * @private
   */
  _addClickHandler() {
    var _this = this;

    this.$element
      .off('click.zf.tabs')
      .on('click.zf.tabs', `.${this.options.linkClass}`, function(e){
        e.preventDefault();
        _this._handleTabChange($(this));
      });
  }

  /**
   * Adds keyboard event handlers for items within the tabs.
   * @private
   */
  _addKeyHandler() {
    var _this = this;

    this.$tabTitles.off('keydown.zf.tabs').on('keydown.zf.tabs', function(e){
      if (e.which === 9) return;

      var $element = $(this),
        $elements = $element.parent('ul').children('li'),
        $prevElement,
        $nextElement;

      $elements.each(function(i) {
        if ($(this).is($element)) {
          if (_this.options.wrapOnKeys) {
            $prevElement = i === 0 ? $elements.last() : $elements.eq(i-1);
            $nextElement = i === $elements.length -1 ? $elements.first() : $elements.eq(i+1);
          } else {
            $prevElement = $elements.eq(Math.max(0, i-1));
            $nextElement = $elements.eq(Math.min(i+1, $elements.length-1));
          }
          return;
        }
      });

      // handle keyboard event with keyboard util
      Keyboard.handleKey(e, 'Tabs', {
        open: function() {
          $element.find('[role="tab"]').focus();
          _this._handleTabChange($element);
        },
        previous: function() {
          $prevElement.find('[role="tab"]').focus();
          _this._handleTabChange($prevElement);
        },
        next: function() {
          $nextElement.find('[role="tab"]').focus();
          _this._handleTabChange($nextElement);
        },
        handled: function() {
          e.preventDefault();
        }
      });
    });
  }

  /**
   * Opens the tab `$targetContent` defined by `$target`. Collapses active tab.
   * @param {jQuery} $target - Tab to open.
   * @param {boolean} historyHandled - browser has already handled a history update
   * @fires Tabs#change
   * @function
   */
  _handleTabChange($target, historyHandled) {

    /**
     * Check for active class on target. Collapse if exists.
     */
    if ($target.hasClass(`${this.options.linkActiveClass}`)) {
        if(this.options.activeCollapse) {
            this._collapseTab($target);

           /**
            * Fires when the zplugin has successfully collapsed tabs.
            * @event Tabs#collapse
            */
            this.$element.trigger('collapse.zf.tabs', [$target]);
        }
        return;
    }

    var $oldTab = this.$element.
          find(`.${this.options.linkClass}.${this.options.linkActiveClass}`),
          $tabLink = $target.find('[role="tab"]'),
          hash = $tabLink.attr('data-tabs-target') || $tabLink[0].hash.slice(1),
          $targetContent = this.$tabContent.find(`#${hash}`);

    //close old tab
    this._collapseTab($oldTab);

    //open new tab
    this._openTab($target);

    //either replace or update browser history
    if (this.options.deepLink && !historyHandled) {
      var anchor = $target.find('a').attr('href');

      if (this.options.updateHistory) {
        history.pushState({}, '', anchor);
      } else {
        history.replaceState({}, '', anchor);
      }
    }

    /**
     * Fires when the plugin has successfully changed tabs.
     * @event Tabs#change
     */
    this.$element.trigger('change.zf.tabs', [$target, $targetContent]);

    //fire to children a mutation event
    $targetContent.find("[data-mutate]").trigger("mutateme.zf.trigger");
  }

  /**
   * Opens the tab `$targetContent` defined by `$target`.
   * @param {jQuery} $target - Tab to open.
   * @function
   */
  _openTab($target) {
      var $tabLink = $target.find('[role="tab"]'),
          hash = $tabLink.attr('data-tabs-target') || $tabLink[0].hash.slice(1),
          $targetContent = this.$tabContent.find(`#${hash}`);

      $target.addClass(`${this.options.linkActiveClass}`);

      $tabLink.attr({
        'aria-selected': 'true',
        'tabindex': '0'
      });

      $targetContent
        .addClass(`${this.options.panelActiveClass}`).removeAttr('aria-hidden');
  }

  /**
   * Collapses `$targetContent` defined by `$target`.
   * @param {jQuery} $target - Tab to collapse.
   * @function
   */
  _collapseTab($target) {
    var $target_anchor = $target
      .removeClass(`${this.options.linkActiveClass}`)
      .find('[role="tab"]')
      .attr({
        'aria-selected': 'false',
        'tabindex': -1
      });

    $(`#${$target_anchor.attr('aria-controls')}`)
      .removeClass(`${this.options.panelActiveClass}`)
      .attr({ 'aria-hidden': 'true' })
  }

  /**
   * Public method for selecting a content pane to display.
   * @param {jQuery | String} elem - jQuery object or string of the id of the pane to display.
   * @param {boolean} historyHandled - browser has already handled a history update
   * @function
   */
  selectTab(elem, historyHandled) {
    var idStr, hashIdStr;

    if (typeof elem === 'object') {
      idStr = elem[0].id;
    } else {
      idStr = elem;
    }

    if (idStr.indexOf('#') < 0) {
      hashIdStr = `#${idStr}`;
    } else {
      hashIdStr = idStr;
      idStr = idStr.slice(1);
    }

    var $target = this.$tabTitles.find(`[href$="${hashIdStr}"],[data-tabs-target="${idStr}"]`).first().parent(`.${this.options.linkClass}`);

    this._handleTabChange($target, historyHandled);
  };
  /**
   * Sets the height of each panel to the height of the tallest panel.
   * If enabled in options, gets called on media query change.
   * If loading content via external source, can be called directly or with _reflow.
   * If enabled with `data-match-height="true"`, tabs sets to equal height
   * @function
   * @private
   */
  _setHeight() {
    var max = 0,
        _this = this; // Lock down the `this` value for the root tabs object

    this.$tabContent
      .find(`.${this.options.panelClass}`)
      .css('height', '')
      .each(function() {

        var panel = $(this),
            isActive = panel.hasClass(`${_this.options.panelActiveClass}`); // get the options from the parent instead of trying to get them from the child

        if (!isActive) {
          panel.css({'visibility': 'hidden', 'display': 'block'});
        }

        var temp = this.getBoundingClientRect().height;

        if (!isActive) {
          panel.css({
            'visibility': '',
            'display': ''
          });
        }

        max = temp > max ? temp : max;
      })
      .css('height', `${max}px`);
  }

  /**
   * Destroys an instance of tabs.
   * @fires Tabs#destroyed
   */
  _destroy() {
    this.$element
      .find(`.${this.options.linkClass}`)
      .off('.zf.tabs').hide().end()
      .find(`.${this.options.panelClass}`)
      .hide();

    if (this.options.matchHeight) {
      if (this._setHeightMqHandler != null) {
         $(window).off('changed.zf.mediaquery', this._setHeightMqHandler);
      }
    }

    if (this.options.deepLink) {
      $(window).off('hashchange', this._checkDeepLink);
    }

    if (this.onLoadListener) {
      $(window).off(this.onLoadListener);
    }
  }
}

Tabs.defaults = {
  /**
   * Link the location hash to the active pane.
   * Set the location hash when the active pane changes, and open the corresponding pane when the location changes.
   * @option
   * @type {boolean}
   * @default false
   */
  deepLink: false,

  /**
   * If `deepLink` is enabled, adjust the deep link scroll to make sure the top of the tab panel is visible
   * @option
   * @type {boolean}
   * @default false
   */
  deepLinkSmudge: false,

  /**
   * If `deepLinkSmudge` is enabled, animation time (ms) for the deep link adjustment
   * @option
   * @type {number}
   * @default 300
   */
  deepLinkSmudgeDelay: 300,

  /**
   * If `deepLink` is enabled, update the browser history with the open tab
   * @option
   * @type {boolean}
   * @default false
   */
  updateHistory: false,

  /**
   * Allows the window to scroll to content of active pane on load.
   * Not recommended if more than one tab panel per page.
   * @option
   * @type {boolean}
   * @default false
   */
  autoFocus: false,

  /**
   * Allows keyboard input to 'wrap' around the tab links.
   * @option
   * @type {boolean}
   * @default true
   */
  wrapOnKeys: true,

  /**
   * Allows the tab content panes to match heights if set to true.
   * @option
   * @type {boolean}
   * @default false
   */
  matchHeight: false,

  /**
   * Allows active tabs to collapse when clicked.
   * @option
   * @type {boolean}
   * @default false
   */
  activeCollapse: false,

  /**
   * Class applied to `li`'s in tab link list.
   * @option
   * @type {string}
   * @default 'tabs-title'
   */
  linkClass: 'tabs-title',

  /**
   * Class applied to the active `li` in tab link list.
   * @option
   * @type {string}
   * @default 'is-active'
   */
  linkActiveClass: 'is-active',

  /**
   * Class applied to the content containers.
   * @option
   * @type {string}
   * @default 'tabs-panel'
   */
  panelClass: 'tabs-panel',

  /**
   * Class applied to the active content container.
   * @option
   * @type {string}
   * @default 'is-active'
   */
  panelActiveClass: 'is-active'
};

export {Tabs};
<commit_msg>feat: add "Tabs._collapse()" to collapse the active tab<commit_after>'use strict';

import $ from 'jquery';
import { Plugin } from './foundation.core.plugin';
import { onLoad } from './foundation.core.utils';
import { Keyboard } from './foundation.util.keyboard';
import { onImagesLoaded } from './foundation.util.imageLoader';
/**
 * Tabs module.
 * @module foundation.tabs
 * @requires foundation.util.keyboard
 * @requires foundation.util.imageLoader if tabs contain images
 */

class Tabs extends Plugin {
  /**
   * Creates a new instance of tabs.
   * @class
   * @name Tabs
   * @fires Tabs#init
   * @param {jQuery} element - jQuery object to make into tabs.
   * @param {Object} options - Overrides to the default plugin settings.
   */
  _setup(element, options) {
    this.$element = element;
    this.options = $.extend({}, Tabs.defaults, this.$element.data(), options);
    this.className = 'Tabs'; // ie9 back compat

    this._init();
    Keyboard.register('Tabs', {
      'ENTER': 'open',
      'SPACE': 'open',
      'ARROW_RIGHT': 'next',
      'ARROW_UP': 'previous',
      'ARROW_DOWN': 'next',
      'ARROW_LEFT': 'previous'
      // 'TAB': 'next',
      // 'SHIFT_TAB': 'previous'
    });
  }

  /**
   * Initializes the tabs by showing and focusing (if autoFocus=true) the preset active tab.
   * @private
   */
  _init() {
    var _this = this;

    this.$element.attr({'role': 'tablist'});
    this.$tabTitles = this.$element.find(`.${this.options.linkClass}`);
    this.$tabContent = $(`[data-tabs-content="${this.$element[0].id}"]`);

    this.$tabTitles.each(function(){
      var $elem = $(this),
          $link = $elem.find('a'),
          isActive = $elem.hasClass(`${_this.options.linkActiveClass}`),
          hash = $link.attr('data-tabs-target') || $link[0].hash.slice(1),
          linkId = $link[0].id ? $link[0].id : `${hash}-label`,
          $tabContent = $(`#${hash}`);

      $elem.attr({'role': 'presentation'});

      $link.attr({
        'role': 'tab',
        'aria-controls': hash,
        'aria-selected': isActive,
        'id': linkId,
        'tabindex': isActive ? '0' : '-1'
      });

      $tabContent.attr({
        'role': 'tabpanel',
        'aria-labelledby': linkId
      });

      // Save up the initial hash to return to it later when going back in history
      if (isActive) {
        _this._initialAnchor = `#${hash}`;
      }

      if(!isActive) {
        $tabContent.attr('aria-hidden', 'true');
      }

      if(isActive && _this.options.autoFocus){
        _this.onLoadListener = onLoad($(window), function() {
          $('html, body').animate({ scrollTop: $elem.offset().top }, _this.options.deepLinkSmudgeDelay, () => {
            $link.focus();
          });
        });
      }
    });

    if(this.options.matchHeight) {
      var $images = this.$tabContent.find('img');

      if ($images.length) {
        onImagesLoaded($images, this._setHeight.bind(this));
      } else {
        this._setHeight();
      }
    }

     //current context-bound function to open tabs on page load or history hashchange
    this._checkDeepLink = () => {
      var anchor = window.location.hash;

      // if there is no anchor, return to the initial tab
      if (!anchor.length && this._initialAnchor) {
        anchor = this._initialAnchor;
      }

      //need a hash and a relevant anchor in this tabset
      if (anchor.length) {
        var anchorNoHash = (anchor.indexOf('#') >= 0 ? anchor.slice(1) : anchor);
        var $link = this.$element.find(`[href$="${anchor}"],[data-tabs-target="${anchorNoHash}"]`).first();
        if ($link.length) {
          this.selectTab($(anchor), true);

          //roll up a little to show the titles
          if (this.options.deepLinkSmudge) {
            var offset = this.$element.offset();
            $('html, body').animate({ scrollTop: offset.top }, this.options.deepLinkSmudgeDelay);
          }

          /**
            * Fires when the zplugin has deeplinked at pageload
            * @event Tabs#deeplink
            */
           this.$element.trigger('deeplink.zf.tabs', [$link, $(anchor)]);
         }
       }
     }

    //use browser to open a tab, if it exists in this tabset
    if (this.options.deepLink) {
      this._checkDeepLink();
    }

    this._events();
  }

  /**
   * Adds event handlers for items within the tabs.
   * @private
   */
  _events() {
    this._addKeyHandler();
    this._addClickHandler();
    this._setHeightMqHandler = null;

    if (this.options.matchHeight) {
      this._setHeightMqHandler = this._setHeight.bind(this);

      $(window).on('changed.zf.mediaquery', this._setHeightMqHandler);
    }

    if(this.options.deepLink) {
      $(window).on('hashchange', this._checkDeepLink);
    }
  }

  /**
   * Adds click handlers for items within the tabs.
   * @private
   */
  _addClickHandler() {
    var _this = this;

    this.$element
      .off('click.zf.tabs')
      .on('click.zf.tabs', `.${this.options.linkClass}`, function(e){
        e.preventDefault();
        _this._handleTabChange($(this));
      });
  }

  /**
   * Adds keyboard event handlers for items within the tabs.
   * @private
   */
  _addKeyHandler() {
    var _this = this;

    this.$tabTitles.off('keydown.zf.tabs').on('keydown.zf.tabs', function(e){
      if (e.which === 9) return;

      var $element = $(this),
        $elements = $element.parent('ul').children('li'),
        $prevElement,
        $nextElement;

      $elements.each(function(i) {
        if ($(this).is($element)) {
          if (_this.options.wrapOnKeys) {
            $prevElement = i === 0 ? $elements.last() : $elements.eq(i-1);
            $nextElement = i === $elements.length -1 ? $elements.first() : $elements.eq(i+1);
          } else {
            $prevElement = $elements.eq(Math.max(0, i-1));
            $nextElement = $elements.eq(Math.min(i+1, $elements.length-1));
          }
          return;
        }
      });

      // handle keyboard event with keyboard util
      Keyboard.handleKey(e, 'Tabs', {
        open: function() {
          $element.find('[role="tab"]').focus();
          _this._handleTabChange($element);
        },
        previous: function() {
          $prevElement.find('[role="tab"]').focus();
          _this._handleTabChange($prevElement);
        },
        next: function() {
          $nextElement.find('[role="tab"]').focus();
          _this._handleTabChange($nextElement);
        },
        handled: function() {
          e.preventDefault();
        }
      });
    });
  }

  /**
   * Opens the tab `$targetContent` defined by `$target`. Collapses active tab.
   * @param {jQuery} $target - Tab to open.
   * @param {boolean} historyHandled - browser has already handled a history update
   * @fires Tabs#change
   * @function
   */
  _handleTabChange($target, historyHandled) {

    // With `activeCollapse`, if the target is the active Tab, collapse it.
    if ($target.hasClass(`${this.options.linkActiveClass}`)) {
        if(this.options.activeCollapse) {
            this._collapse();
        }
        return;
    }

    var $oldTab = this.$element.
          find(`.${this.options.linkClass}.${this.options.linkActiveClass}`),
          $tabLink = $target.find('[role="tab"]'),
          hash = $tabLink.attr('data-tabs-target') || $tabLink[0].hash.slice(1),
          $targetContent = this.$tabContent.find(`#${hash}`);

    //close old tab
    this._collapseTab($oldTab);

    //open new tab
    this._openTab($target);

    //either replace or update browser history
    if (this.options.deepLink && !historyHandled) {
      var anchor = $target.find('a').attr('href');

      if (this.options.updateHistory) {
        history.pushState({}, '', anchor);
      } else {
        history.replaceState({}, '', anchor);
      }
    }

    /**
     * Fires when the plugin has successfully changed tabs.
     * @event Tabs#change
     */
    this.$element.trigger('change.zf.tabs', [$target, $targetContent]);

    //fire to children a mutation event
    $targetContent.find("[data-mutate]").trigger("mutateme.zf.trigger");
  }

  /**
   * Opens the tab `$targetContent` defined by `$target`.
   * @param {jQuery} $target - Tab to open.
   * @function
   */
  _openTab($target) {
      var $tabLink = $target.find('[role="tab"]'),
          hash = $tabLink.attr('data-tabs-target') || $tabLink[0].hash.slice(1),
          $targetContent = this.$tabContent.find(`#${hash}`);

      $target.addClass(`${this.options.linkActiveClass}`);

      $tabLink.attr({
        'aria-selected': 'true',
        'tabindex': '0'
      });

      $targetContent
        .addClass(`${this.options.panelActiveClass}`).removeAttr('aria-hidden');
  }

  /**
   * Collapses `$targetContent` defined by `$target`.
   * @param {jQuery} $target - Tab to collapse.
   * @function
   */
  _collapseTab($target) {
    var $target_anchor = $target
      .removeClass(`${this.options.linkActiveClass}`)
      .find('[role="tab"]')
      .attr({
        'aria-selected': 'false',
        'tabindex': -1
      });

    $(`#${$target_anchor.attr('aria-controls')}`)
      .removeClass(`${this.options.panelActiveClass}`)
      .attr({ 'aria-hidden': 'true' })
  }

  /**
   * Collapses the active Tab.
   * @fires Tabs#collapse
   * @function
   */
  _collapse() {
    var $activeTab = this.$element.find(`.${this.options.linkClass}.${this.options.linkActiveClass}`);

    if ($activeTab.length) {
      this._collapseTab($activeTab);

      /**
      * Fires when the zplugin has successfully collapsed tabs.
      * @event Tabs#collapse
      */
      this.$element.trigger('collapse.zf.tabs', [$activeTab]);
    }
  }

  /**
   * Public method for selecting a content pane to display.
   * @param {jQuery | String} elem - jQuery object or string of the id of the pane to display.
   * @param {boolean} historyHandled - browser has already handled a history update
   * @function
   */
  selectTab(elem, historyHandled) {
    var idStr, hashIdStr;

    if (typeof elem === 'object') {
      idStr = elem[0].id;
    } else {
      idStr = elem;
    }

    if (idStr.indexOf('#') < 0) {
      hashIdStr = `#${idStr}`;
    } else {
      hashIdStr = idStr;
      idStr = idStr.slice(1);
    }

    var $target = this.$tabTitles.find(`[href$="${hashIdStr}"],[data-tabs-target="${idStr}"]`).first().parent(`.${this.options.linkClass}`);

    this._handleTabChange($target, historyHandled);
  };
  /**
   * Sets the height of each panel to the height of the tallest panel.
   * If enabled in options, gets called on media query change.
   * If loading content via external source, can be called directly or with _reflow.
   * If enabled with `data-match-height="true"`, tabs sets to equal height
   * @function
   * @private
   */
  _setHeight() {
    var max = 0,
        _this = this; // Lock down the `this` value for the root tabs object

    this.$tabContent
      .find(`.${this.options.panelClass}`)
      .css('height', '')
      .each(function() {

        var panel = $(this),
            isActive = panel.hasClass(`${_this.options.panelActiveClass}`); // get the options from the parent instead of trying to get them from the child

        if (!isActive) {
          panel.css({'visibility': 'hidden', 'display': 'block'});
        }

        var temp = this.getBoundingClientRect().height;

        if (!isActive) {
          panel.css({
            'visibility': '',
            'display': ''
          });
        }

        max = temp > max ? temp : max;
      })
      .css('height', `${max}px`);
  }

  /**
   * Destroys an instance of tabs.
   * @fires Tabs#destroyed
   */
  _destroy() {
    this.$element
      .find(`.${this.options.linkClass}`)
      .off('.zf.tabs').hide().end()
      .find(`.${this.options.panelClass}`)
      .hide();

    if (this.options.matchHeight) {
      if (this._setHeightMqHandler != null) {
         $(window).off('changed.zf.mediaquery', this._setHeightMqHandler);
      }
    }

    if (this.options.deepLink) {
      $(window).off('hashchange', this._checkDeepLink);
    }

    if (this.onLoadListener) {
      $(window).off(this.onLoadListener);
    }
  }
}

Tabs.defaults = {
  /**
   * Link the location hash to the active pane.
   * Set the location hash when the active pane changes, and open the corresponding pane when the location changes.
   * @option
   * @type {boolean}
   * @default false
   */
  deepLink: false,

  /**
   * If `deepLink` is enabled, adjust the deep link scroll to make sure the top of the tab panel is visible
   * @option
   * @type {boolean}
   * @default false
   */
  deepLinkSmudge: false,

  /**
   * If `deepLinkSmudge` is enabled, animation time (ms) for the deep link adjustment
   * @option
   * @type {number}
   * @default 300
   */
  deepLinkSmudgeDelay: 300,

  /**
   * If `deepLink` is enabled, update the browser history with the open tab
   * @option
   * @type {boolean}
   * @default false
   */
  updateHistory: false,

  /**
   * Allows the window to scroll to content of active pane on load.
   * Not recommended if more than one tab panel per page.
   * @option
   * @type {boolean}
   * @default false
   */
  autoFocus: false,

  /**
   * Allows keyboard input to 'wrap' around the tab links.
   * @option
   * @type {boolean}
   * @default true
   */
  wrapOnKeys: true,

  /**
   * Allows the tab content panes to match heights if set to true.
   * @option
   * @type {boolean}
   * @default false
   */
  matchHeight: false,

  /**
   * Allows active tabs to collapse when clicked.
   * @option
   * @type {boolean}
   * @default false
   */
  activeCollapse: false,

  /**
   * Class applied to `li`'s in tab link list.
   * @option
   * @type {string}
   * @default 'tabs-title'
   */
  linkClass: 'tabs-title',

  /**
   * Class applied to the active `li` in tab link list.
   * @option
   * @type {string}
   * @default 'is-active'
   */
  linkActiveClass: 'is-active',

  /**
   * Class applied to the content containers.
   * @option
   * @type {string}
   * @default 'tabs-panel'
   */
  panelClass: 'tabs-panel',

  /**
   * Class applied to the active content container.
   * @option
   * @type {string}
   * @default 'is-active'
   */
  panelActiveClass: 'is-active'
};

export {Tabs};
<|endoftext|>


<commit_before>/**
 * Created by SLedunois on 01/05/2016.
 */

/**
 * Json Object containing the app params
 * @type {{client_id: string, client_secret: string, redirect_uri: string}}
 */
var params = {
    "client_id": "client_id",
    "client_secret": "client_secret",
    "redirect_uri": "redirect_uri",
    "access_token" : "access_token",
    "user_id" : "123456789",
    "returned_status_code" : 400
};

var should = require('should');
var inodesta = require('../lib/main').inodesta(params);

describe('#oAuth.getAuthorizeUri()', function () {
    it('Returns a string containing the uri needs to authorize the application.', function () {
        var result = inodesta.oAuth.getAuthorizeUri();
        result.should.eql("https://api.instagram.com/oauth/authorize/?client_id=client_id&redirect_uri=redirect_uri&response_type=code");
    });
});

describe('#user.getSelfUserInfo(access_token, callback)', function() {
    it('Returns an object containing self user informations', function(done) {
       inodesta.user.getSelfUserInfo(params.access_token, function(result) {
           result.should.be.type('object').have.property('meta').have.property('code', params.returned_status_code);
           done();
       });
    });
});

describe('#user.getUserInfo(access_token, user_id, callback)', function() {
    it('Returns an object containing user informations', function(done) {
       inodesta.user.getUserInfo(params.access_token, params.user_id, function(result) {
           result.should.be.type('object').have.property('meta').have.property('code', params.returned_status_code);
           done();
       });
    });
});

describe('#user.getSelfRecentMedia(access_token, count, min_id, max_id, callback)', function() {
    it('Returns an object containing the most recent media published by the owner of the access_token.', function(done) {
        inodesta.user.getSelfRecentMedia(params.access_token, null, null, null, function(result){
            result.should.be.type('object').have.property('meta').have.property('code', params.returned_status_code);
            done();
        });
    })
});

describe('#user.getUserRecentMedia(userId, accessToken, count, minId, maxId, callback)', function() {
  it('Returns an object containing the most recent media published by the provided user id.', function(done) {
    inodesta.user.getUserRecentMedia(params.user_id, params.access_token, null, null, null, function(result) {
      result.should.be.type('object').have.property('meta').have.property('code', params.returned_status_code);
      done();
    });
  });
});

describe('#user.getSeldMediaLiked(accessToken, count, maxLikeId, callback)', function() {
  it('Returns an object containing the list of recent media like by the owner of the accessToken.', function(done) {
    inodesta.user.getSeldMediaLiked(params.access_token, null, null, function(result) {
      result.should.be.type('object').have.property('meta').have.property('code', params.returned_status_code);
      done();
    })
  });
});

describe('#user.search(accessToken, query, callback)', function() {
  it('Returns an object containing a list of user matching with the query', function(done) {
    inodesta.user.search(params.access_token, 'test', function(result) {
      result.should.be.type('object').have.property('meta').have.property('code', params.returned_status_code);
      done();
    })
  });
});

<commit_msg>Adding oAuth access token test<commit_after>/**
 * Created by SLedunois on 01/05/2016.
 */

/**
 * Json Object containing the app params
 * @type {{client_id: string, client_secret: string, redirect_uri: string}}
 */
var params = {
    "client_id": "client_id",
    "client_secret": "client_secret",
    "redirect_uri": "redirect_uri",
    "user_code" : "user_code",
    "access_token" : "access_token",
    "user_id" : "123456789",
    "returned_status_code" : 400
};

var should = require('should');
var inodesta = require('../lib/main').inodesta(params);

describe('#oAuth.getAuthorizeUri()', function () {
    it('Returns a string containing the uri needs to authorize the application.', function () {
        var result = inodesta.oAuth.getAuthorizeUri();
        result.should.eql("https://api.instagram.com/oauth/authorize/?client_id=client_id&redirect_uri=redirect_uri&response_type=code");
    });
});

describe('#oAuth.getAccessToken(code, callback)', function() {
  it('Returns an object containing user access token.', function(done) {
    inodesta.oAuth.getAccessToken(params.user_code, function(result) {
      result.should.be.type('object').have.property('code', params.returned_status_code);
      done();
    });
  });
});

describe('#user.getSelfUserInfo(access_token, callback)', function() {
    it('Returns an object containing self user informations', function(done) {
       inodesta.user.getSelfUserInfo(params.access_token, function(result) {
           result.should.be.type('object').have.property('meta').have.property('code', params.returned_status_code);
           done();
       });
    });
});

describe('#user.getUserInfo(access_token, user_id, callback)', function() {
    it('Returns an object containing user informations', function(done) {
       inodesta.user.getUserInfo(params.access_token, params.user_id, function(result) {
           result.should.be.type('object').have.property('meta').have.property('code', params.returned_status_code);
           done();
       });
    });
});

describe('#user.getSelfRecentMedia(access_token, count, min_id, max_id, callback)', function() {
    it('Returns an object containing the most recent media published by the owner of the access_token.', function(done) {
        inodesta.user.getSelfRecentMedia(params.access_token, null, null, null, function(result){
            result.should.be.type('object').have.property('meta').have.property('code', params.returned_status_code);
            done();
        });
    })
});

describe('#user.getUserRecentMedia(userId, accessToken, count, minId, maxId, callback)', function() {
  it('Returns an object containing the most recent media published by the provided user id.', function(done) {
    inodesta.user.getUserRecentMedia(params.user_id, params.access_token, null, null, null, function(result) {
      result.should.be.type('object').have.property('meta').have.property('code', params.returned_status_code);
      done();
    });
  });
});

describe('#user.getSeldMediaLiked(accessToken, count, maxLikeId, callback)', function() {
  it('Returns an object containing the list of recent media like by the owner of the accessToken.', function(done) {
    inodesta.user.getSeldMediaLiked(params.access_token, null, null, function(result) {
      result.should.be.type('object').have.property('meta').have.property('code', params.returned_status_code);
      done();
    })
  });
});

describe('#user.search(accessToken, query, callback)', function() {
  it('Returns an object containing a list of user matching with the query', function(done) {
    inodesta.user.search(params.access_token, 'test', function(result) {
      result.should.be.type('object').have.property('meta').have.property('code', params.returned_status_code);
      done();
    })
  });
});

<|endoftext|>


<commit_before>// DIRECTIVES
pbpApp.directive("pbpGroup", function() {
    return {
        restrict: 'AE',
        templateUrl: function() {
            var baseUrl = angular.element(document.querySelector("#mst_base_url")).val();
            return baseUrl + "productbuilderpro/directives/pbpgroup"
        },
        replace: true,
        scope: {
            product: "=",
        }
    }
});
pbpApp.directive("pbpOptionCollection", [function() {
    return {
        restrict: 'AE',
        templateUrl: function() {
            var baseUrl = angular.element(document.querySelector("#mst_base_url")).val();
            return baseUrl + "productbuilderpro/directives/pbpOptionCollection"
        },
        replace: true,
        scope: {
            optionList: "=",
            level: "@"
        }
    }
}]);
pbpApp.directive("pbpOptionDetails", ["$compile", "pbpServices", "$location", function($compile, pbpServices, $location) {
    //=== LAYER DETAILS CONTROLLER ===//
    layerController.$inject = ["$scope", "pbpServices", "groupServices", "$rootScope"];
    function layerController($scope, pbpServices, groupServices, $rootScope) {
        $scope.mediaUrl = angular.element(document.querySelector("#mst_media_url")).val() + "pbp/images/";
        $scope.expand = true;
        $scope.editLayer = function(layer) {
            console.info("Edit layer func");
            groupServices.currentLayer = layer;
            $location.path("/add-layer/" + layer.id + '/group/' + layer.group_id);
        }
        $scope.expandLayer = function(layer) {
            $scope.expand = !$scope.expand;
            angular.element(document.querySelector("#layer_" + layer.id + " ul")).toggleClass("ng-hide");
        }
        $scope.removeLayer = function(layer) {
            console.info("Remove layer");
            if(!confirm("Are you really want to delete this layer?")) {
                return false;
            }
            groupServices.removeLayer(layer)
            .then(function(response) {
                groupServices.updateGroupData(response);
            }, function(error) {
                console.warn(error);
            });
        }
        $scope.addLayerToDesign = function(layer) {
            console.info("Add Layer To Design");
            //First time load, layerStack == null
            groupServices.currentGroupId = layer.group_id;
            if(!groupServices.layerStack) {
                groupServices.layerStack = {};
            }
            if(!groupServices.layerStack[layer.group_id]) {
                groupServices.layerStack[layer.group_id] = {};
            }
            //Get group info
            $scope.currentGroupLayers = null;
            angular.forEach(groupServices.groups, function(group, index) {
                if(group.id === layer.group_id) {
                    $scope.currentGroupLayers = group.layers || null;
                    return false;
                }
            });
            //console.log(groupServices.groups);
            if($scope.currentGroupLayers) {
                angular.forEach($scope.currentGroupLayers, function(layerInGroup, index) {
                    if(layer.parent_id === layerInGroup.id) {
                        // 1 is single select
                        // Remove the object have same parent id before add new object to stack
                        if(layerInGroup.select_type === "1") {
                            angular.forEach(groupServices.layerStack[layer.group_id], function(layerInStack, index) {
                                if(layerInStack.parent_id === layerInGroup.id) {
                                    delete groupServices.layerStack[layer.group_id][layerInStack.id];
                                }
                            });
                        }
                    }
                });
            }
            //groupServices.layerStack[layer.group_id][layer.id] = layer;
            groupServices.layerStack[layer.group_id][layer.id] = layer;
            groupServices.updateSelectedLayer(groupServices.layerStack);
        }
    }
    return {
        restrict: 'AE',
        templateUrl: function() {
            var baseUrl = angular.element(document.querySelector("#mst_base_url")).val();
            return baseUrl + "productbuilderpro/directives/pbpOptionDetails"
        },
        replace: true,
        scope: {
            optionInfo: "=",
        },
        link: function (scope, element, attrs, controller) {
            //check if this member has children
            if (angular.isArray(scope.optionInfo.options)) {
                // append the collection directive to this element
                //element.append("<pbp-option-collection option-list='optionInfo.options'></pbp-option-collection>");
                // we need to tell angular to render the directive
                //$compile(element.contents())(scope);
                var newMemEL = angular.element("<pbp-option-collection option-list='optionInfo.options'></pbp-option-collection>");
                element.append(newMemEL);
                $compile(newMemEL)(scope);
            }
            /*element.bind("click", function(event) {
                pbpServices.showLog("Child scope click event", "info");
                //Need to run inside $apply function
                scope.$apply(function() {
                   console.log(event.target);
                    pbpServices.currentOption = scope.optionInfo;
                    $location.path("/edit-option");
                });
                event.stop(); //Stop parent event
            });*/
        },
        controller: layerController
    }
}]);
pbpApp.directive("pbpMedia", ["$compile", "pbpServices", function($compile, pbpServices) {
    //=== MEDIA CONTROLLER ===//
    mediaController.$inject = ["$scope", "pbpServices", "groupServices"];
    function mediaController($scope, pbpServices, groupServices) {
        $scope.baseUrl = angular.element(document.querySelector("#mst_base_url")).val();
        $scope.mediaUrl = angular.element(document.querySelector("#mst_media_url")).val() + "pbp/images/";
        //Image list
        $scope.searchKeyword = '';
        $scope.showMedia = false;
        $scope.isUploading = false;
        $scope.imgSrc = '';
        $scope.selectImage = function(element) {
            $scope.showMedia = !$scope.showMedia;
        }
        $scope.closeImageLibrary = function() {
            $scope.showMedia = false;
        }
        $scope.chooseImage = function(image, inputName) {
            angular.element(document.querySelector("input[name='"+ inputName +"']")).val(image.filename);
            $scope.showMedia = false;
        }
        $scope.removeImage = function() {
            if(!confirm("Are you sure?")) {
                return false;
            }
            $scope.imgSrc = "";
            $scope.$parent[$scope.name] = "";
        }
        $scope.uploadFile = function(){
            var file = $scope.myFile;
            if(file === undefined) {
                return false;
            }
            $scope.isUploading = true;
            groupServices.uploadImage(file)
            .then(function(response) {
                //Update filename to parent scope
                $scope.$parent[$scope.name] = response.filename;
                $scope.imgSrc = response.filename;
                $scope.isUploading = false;
                //Upload same file once
                $scope.myFile = undefined;
            }, function(error) {
                console.warn(error);
            });
        };
    }
    return {
        restrict: 'AE',
        templateUrl: function() {
            var baseUrl = angular.element(document.querySelector("#mst_base_url")).val();
            return baseUrl + "productbuilderpro/directives/pbpMedia"
        },
        replace: true,
        link: function ($scope, element, attrs, controller) {
            /*element.bind("click", function(event) {
                if(event.target.src) {
                    var imgSrcSelector = angular.element(document.querySelector(".img-src"));
                    imgSrcSelector.attr("value", event.target.src.split("/pbp/images/")[1]);
                    $scope.showMedia = false;
                    console.log(imgSrcSelector);
                }
                //console.log(event.target);
                //event.stop(); //Stop parent event
            });*/
        },
        controller: mediaController,
        scope: {
            name: '@',
            imgSrc: '@',
            libraryImages: '='
        }
    }
}]);
//==== FILE UPLOAD DIRECTIVE ====//
pbpApp.directive('fileModel', ['$parse', function ($parse) {
    return {
        restrict: 'A',
        link: function(scope, element, attrs) {
            var model = $parse(attrs.fileModel);
            var modelSetter = model.assign;
            element.bind('change', function(){
                scope.$apply(function(){
                    modelSetter(scope, element[0].files[0]);
                });
            });
        }
    };
}]);
//==== LOADING DIRECTIVE ====//
pbpApp.directive("pbpLoading", [function() {
    this.media_url = document.getElementById("mst_media_url").value + "pbp/images/";
    return {
        restrict: 'AE',
        template: '',
        replace: true
    }
}]);
//==== DOWNLOADING DIRECTIVE ====//
pbpApp.directive("pbpDownloading", [function() {
    this.media_url = document.getElementById("mst_media_url").value + "pbp/images/";
    return {
        restrict: 'AE',
        template: '',
        replace: true
    }
}]);
//==== SELECTED LAYER ====//
pbpApp.directive("selectedLayer", ["$compile", "pbpServices", "$rootScope", function($compile, pbpServices, $rootScope) {
    //=== SELECTED LAYER CONTROLLER ===//
    selectedLayer.$inject = ["$scope", "pbpServices", "groupServices", "$rootScope"];
    function selectedLayer($scope, pbpServices, groupServices, $rootScope) {
        $scope.baseUrl = angular.element(document.querySelector("#mst_base_url")).val();
        $scope.mediaUrl = angular.element(document.querySelector("#mst_media_url")).val() + "pbp/images/";
        $scope.getTotalPrice = function(selectedLayers) {
            var total = 0;
            if(selectedLayers) {
                angular.forEach(selectedLayers, function(layer, index) {
                    total += parseFloat(layer.price);
                });
            }
            return total.toFixed(2);
        }
        $scope.showLayer = function(stackLayer) {
            if(angular.equals({}, stackLayer) || !stackLayer) {
                return false;
            }
            return true;
        }
        $scope.removeSelectedLayer = function(layer) {
            groupServices.currentGroupId = layer.group_id;
            delete groupServices.layerStack[layer.group_id][layer.id];
            //Update layerStack via services
            groupServices.updateSelectedLayer(groupServices.layerStack);
        }
    }
    return {
        restrict: 'AE',
        templateUrl: function() {
            var baseUrl = angular.element(document.querySelector("#mst_base_url")).val();
            return baseUrl + "productbuilderpro/directives/selectedLayer"
        },
        replace: true,
        link: function ($scope, element, attrs, controller) {

        },
        scope: {
            groupId : "@",
            layerStack: "=",
            reloadPrice: "&"
        },
        controller: selectedLayer,
    }
}]);<commit_msg>Allow choose image from media library<commit_after>// DIRECTIVES
pbpApp.directive("pbpGroup", function() {
    return {
        restrict: 'AE',
        templateUrl: function() {
            var baseUrl = angular.element(document.querySelector("#mst_base_url")).val();
            return baseUrl + "productbuilderpro/directives/pbpgroup"
        },
        replace: true,
        scope: {
            product: "=",
        }
    }
});
pbpApp.directive("pbpOptionCollection", [function() {
    return {
        restrict: 'AE',
        templateUrl: function() {
            var baseUrl = angular.element(document.querySelector("#mst_base_url")).val();
            return baseUrl + "productbuilderpro/directives/pbpOptionCollection"
        },
        replace: true,
        scope: {
            optionList: "=",
            level: "@"
        }
    }
}]);
pbpApp.directive("pbpOptionDetails", ["$compile", "pbpServices", "$location", function($compile, pbpServices, $location) {
    //=== LAYER DETAILS CONTROLLER ===//
    layerController.$inject = ["$scope", "pbpServices", "groupServices", "$rootScope"];
    function layerController($scope, pbpServices, groupServices, $rootScope) {
        $scope.mediaUrl = angular.element(document.querySelector("#mst_media_url")).val() + "pbp/images/";
        $scope.expand = true;
        $scope.editLayer = function(layer) {
            console.info("Edit layer func");
            groupServices.currentLayer = layer;
            $location.path("/add-layer/" + layer.id + '/group/' + layer.group_id);
        }
        $scope.expandLayer = function(layer) {
            $scope.expand = !$scope.expand;
            angular.element(document.querySelector("#layer_" + layer.id + " ul")).toggleClass("ng-hide");
        }
        $scope.removeLayer = function(layer) {
            console.info("Remove layer");
            if(!confirm("Are you really want to delete this layer?")) {
                return false;
            }
            groupServices.removeLayer(layer)
            .then(function(response) {
                groupServices.updateGroupData(response);
            }, function(error) {
                console.warn(error);
            });
        }
        $scope.addLayerToDesign = function(layer) {
            console.info("Add Layer To Design");
            //First time load, layerStack == null
            groupServices.currentGroupId = layer.group_id;
            if(!groupServices.layerStack) {
                groupServices.layerStack = {};
            }
            if(!groupServices.layerStack[layer.group_id]) {
                groupServices.layerStack[layer.group_id] = {};
            }
            //Get group info
            $scope.currentGroupLayers = null;
            angular.forEach(groupServices.groups, function(group, index) {
                if(group.id === layer.group_id) {
                    $scope.currentGroupLayers = group.layers || null;
                    return false;
                }
            });
            //console.log(groupServices.groups);
            if($scope.currentGroupLayers) {
                angular.forEach($scope.currentGroupLayers, function(layerInGroup, index) {
                    if(layer.parent_id === layerInGroup.id) {
                        // 1 is single select
                        // Remove the object have same parent id before add new object to stack
                        if(layerInGroup.select_type === "1") {
                            angular.forEach(groupServices.layerStack[layer.group_id], function(layerInStack, index) {
                                if(layerInStack.parent_id === layerInGroup.id) {
                                    delete groupServices.layerStack[layer.group_id][layerInStack.id];
                                }
                            });
                        }
                    }
                });
            }
            //groupServices.layerStack[layer.group_id][layer.id] = layer;
            groupServices.layerStack[layer.group_id][layer.id] = layer;
            groupServices.updateSelectedLayer(groupServices.layerStack);
        }
    }
    return {
        restrict: 'AE',
        templateUrl: function() {
            var baseUrl = angular.element(document.querySelector("#mst_base_url")).val();
            return baseUrl + "productbuilderpro/directives/pbpOptionDetails"
        },
        replace: true,
        scope: {
            optionInfo: "=",
        },
        link: function (scope, element, attrs, controller) {
            //check if this member has children
            if (angular.isArray(scope.optionInfo.options)) {
                // append the collection directive to this element
                //element.append("<pbp-option-collection option-list='optionInfo.options'></pbp-option-collection>");
                // we need to tell angular to render the directive
                //$compile(element.contents())(scope);
                var newMemEL = angular.element("<pbp-option-collection option-list='optionInfo.options'></pbp-option-collection>");
                element.append(newMemEL);
                $compile(newMemEL)(scope);
            }
            /*element.bind("click", function(event) {
                pbpServices.showLog("Child scope click event", "info");
                //Need to run inside $apply function
                scope.$apply(function() {
                   console.log(event.target);
                    pbpServices.currentOption = scope.optionInfo;
                    $location.path("/edit-option");
                });
                event.stop(); //Stop parent event
            });*/
        },
        controller: layerController
    }
}]);
pbpApp.directive("pbpMedia", ["$compile", "pbpServices", function($compile, pbpServices) {
    //=== MEDIA CONTROLLER ===//
    mediaController.$inject = ["$scope", "pbpServices", "groupServices"];
    function mediaController($scope, pbpServices, groupServices) {
        $scope.baseUrl = angular.element(document.querySelector("#mst_base_url")).val();
        $scope.mediaUrl = angular.element(document.querySelector("#mst_media_url")).val() + "pbp/images/";
        //Image list
        $scope.searchKeyword = '';
        $scope.showMedia = false;
        $scope.isUploading = false;
        $scope.imgSrc = '';
        $scope.selectImage = function(element) {
            $scope.showMedia = !$scope.showMedia;
        }
        $scope.closeImageLibrary = function() {
            $scope.showMedia = false;
        }
        $scope.chooseImage = function(image, inputName) {
            //angular.element(document.querySelector("input[name='"+ inputName +"']")).val(image.filename);
            $scope.$parent[$scope.name] = image.filename;
            $scope.imgSrc = image.filename;
            $scope.showMedia = false;
        }
        $scope.removeImage = function() {
            if(!confirm("Are you sure?")) {
                return false;
            }
            $scope.imgSrc = "";
            $scope.$parent[$scope.name] = "";
        }
        $scope.uploadFile = function(){
            var file = $scope.myFile;
            if(file === undefined) {
                return false;
            }
            $scope.isUploading = true;
            groupServices.uploadImage(file)
            .then(function(response) {
                //Update filename to parent scope
                $scope.$parent[$scope.name] = response.filename;
                $scope.imgSrc = response.filename;
                $scope.isUploading = false;
                //Upload same file once
                $scope.myFile = undefined;
            }, function(error) {
                console.warn(error);
            });
        };
    }
    return {
        restrict: 'AE',
        templateUrl: function() {
            var baseUrl = angular.element(document.querySelector("#mst_base_url")).val();
            return baseUrl + "productbuilderpro/directives/pbpMedia"
        },
        replace: true,
        link: function ($scope, element, attrs, controller) {
            /*element.bind("click", function(event) {
                if(event.target.src) {
                    var imgSrcSelector = angular.element(document.querySelector(".img-src"));
                    imgSrcSelector.attr("value", event.target.src.split("/pbp/images/")[1]);
                    $scope.showMedia = false;
                    console.log(imgSrcSelector);
                }
                //console.log(event.target);
                //event.stop(); //Stop parent event
            });*/
        },
        controller: mediaController,
        scope: {
            name: '@',
            imgSrc: '@',
            libraryImages: '='
        }
    }
}]);
//==== FILE UPLOAD DIRECTIVE ====//
pbpApp.directive('fileModel', ['$parse', function ($parse) {
    return {
        restrict: 'A',
        link: function(scope, element, attrs) {
            var model = $parse(attrs.fileModel);
            var modelSetter = model.assign;
            element.bind('change', function(){
                scope.$apply(function(){
                    modelSetter(scope, element[0].files[0]);
                });
            });
        }
    };
}]);
//==== LOADING DIRECTIVE ====//
pbpApp.directive("pbpLoading", [function() {
    this.media_url = document.getElementById("mst_media_url").value + "pbp/images/";
    return {
        restrict: 'AE',
        template: '',
        replace: true
    }
}]);
//==== DOWNLOADING DIRECTIVE ====//
pbpApp.directive("pbpDownloading", [function() {
    this.media_url = document.getElementById("mst_media_url").value + "pbp/images/";
    return {
        restrict: 'AE',
        template: '',
        replace: true
    }
}]);
//==== SELECTED LAYER ====//
pbpApp.directive("selectedLayer", ["$compile", "pbpServices", "$rootScope", function($compile, pbpServices, $rootScope) {
    //=== SELECTED LAYER CONTROLLER ===//
    selectedLayer.$inject = ["$scope", "pbpServices", "groupServices", "$rootScope"];
    function selectedLayer($scope, pbpServices, groupServices, $rootScope) {
        $scope.baseUrl = angular.element(document.querySelector("#mst_base_url")).val();
        $scope.mediaUrl = angular.element(document.querySelector("#mst_media_url")).val() + "pbp/images/";
        $scope.getTotalPrice = function(selectedLayers) {
            var total = 0;
            if(selectedLayers) {
                angular.forEach(selectedLayers, function(layer, index) {
                    total += parseFloat(layer.price);
                });
            }
            return total.toFixed(2);
        }
        $scope.showLayer = function(stackLayer) {
            if(angular.equals({}, stackLayer) || !stackLayer) {
                return false;
            }
            return true;
        }
        $scope.removeSelectedLayer = function(layer) {
            groupServices.currentGroupId = layer.group_id;
            delete groupServices.layerStack[layer.group_id][layer.id];
            //Update layerStack via services
            groupServices.updateSelectedLayer(groupServices.layerStack);
        }
    }
    return {
        restrict: 'AE',
        templateUrl: function() {
            var baseUrl = angular.element(document.querySelector("#mst_base_url")).val();
            return baseUrl + "productbuilderpro/directives/selectedLayer"
        },
        replace: true,
        link: function ($scope, element, attrs, controller) {

        },
        scope: {
            groupId : "@",
            layerStack: "=",
            reloadPrice: "&"
        },
        controller: selectedLayer,
    }
}]);<|endoftext|>


<commit_before>/* eslint-disable max-nested-callbacks */

import chai from 'chai';
import uuid from 'uuid';

const assert = chai.assert;

import testUsers from '@ciscospark/test-helper-test-users';
import CiscoSpark from '@ciscospark/spark-core';

describe(`Widget Message Meet`, () => {
  let mccoy, spock;
  before(`setup browser and users`, () =>
    testUsers.create({count: 2})
      .then((users) => {
        [mccoy, spock] = users;
        spock.spark = new CiscoSpark({
          credentials: {
            authorization: spock.token
          }
        });

        mccoy.spark = new CiscoSpark({
          credentials: {
            authorization: mccoy.token
          }
        });

        console.info(`Opening widget with token: ${spock.token.access_token}`);
        console.info(`Opening widget to user: ${mccoy.email}`);
        return browser
          .url(`/widget-message-meet`)
          .execute((localAccessToken, localToUserEmail) => {
            window.openWidget(localAccessToken, localToUserEmail);
          }, spock.token.access_token, mccoy.email);

      }));

  // Leaves the browser open for further testing and inspection
  after(() => browser.waitUntil(() => false, 120000));

  it(`should have the right page title`, () => {
    const title = browser.getTitle();
    assert.equal(title, `Widget Message Meet Test`);
  });

  describe(`widget loaded`, () => {
    before(`make sure widget is loaded before testing`, () => {
      browser
        .waitForText(`h1=${mccoy.displayName}`, 60000);
    });

    it(`should have the user's name in title bar`, () => {
      const title = browser.getText(`h1=${mccoy.displayName}`);
      assert.equal(title, mccoy.displayName);
    });

    describe(`conversation loaded`, () => {
      const messageComposer = `.ciscospark-message-composer`;
      const textarea = `textarea.ciscospark-textarea`;
      const sendText = `e2e: ${uuid.v4()}`;

      before(() => {
        $(messageComposer).waitForVisible(30000);
      });

      it(`should have a send message textarea`, () => {
        assert.isTrue(browser.isVisible(textarea));
      });

      it(`should fill the textarea`, () => {
        browser.setValue(textarea, sendText);
        assert.equal(browser.getValue(textarea), sendText, `textarea equals sent text`);
      });

    });
  });
});
<commit_msg>WIP: tests<commit_after>/* eslint-disable max-nested-callbacks */

import chai from 'chai';
import uuid from 'uuid';

const assert = chai.assert;

import testUsers from '@ciscospark/test-helper-test-users';
import CiscoSpark from '@ciscospark/spark-core';

describe(`Widget Message Meet`, () => {
  let mccoy, spock;
  const scopes = `spark:kms spark:rooms_read spark:rooms_write spark:memberships_read spark:memberships_write spark:messages_read spark:messages_write`;
  before(`setup browser and users`, () =>
    testUsers.create({count: 2, scopes})
      .then((users) => {
        [mccoy, spock] = users;
        spock.spark = new CiscoSpark({
          credentials: {
            authorization: spock.token
          }
        });

        mccoy.spark = new CiscoSpark({
          credentials: {
            authorization: mccoy.token
          }
        });
        console.info(`RUN THIS:`);
        console.info(`window.openWidget("${spock.token.access_token}", "${mccoy.email}";)`);
        console.info(`END: RUN THIS`);
        return browser
          .url(`/widget-message-meet`)
          .execute((localAccessToken, localToUserEmail) => {
            window.openWidget(localAccessToken, localToUserEmail);
          }, spock.token.access_token, mccoy.email);

      }));

  // Leaves the browser open for further testing and inspection
  after(() => browser.waitUntil(() => false, 120000, `done waiting: bye`, 10000));

  it(`should have the right page title`, () => {
    const title = browser.getTitle();
    assert.equal(title, `Widget Message Meet Test`);
  });

  describe(`widget loaded`, () => {
    before(`make sure widget is loaded before testing`, () => {
      browser
        .waitForText(`h1=${mccoy.displayName}`, 60000);
    });

    it(`should have the user's name in title bar`, () => {
      const title = browser.getText(`h1=${mccoy.displayName}`);
      assert.equal(title, mccoy.displayName);
    });

    describe(`conversation loaded`, () => {
      const messageComposer = `.ciscospark-message-composer`;
      const textarea = `textarea.ciscospark-textarea`;
      const sendText = `e2e: ${uuid.v4()}`;

      before(() => {
        $(messageComposer).waitForVisible(30000);
      });

      it(`should have a send message textarea`, () => {
        assert.isTrue(browser.isVisible(textarea));
      });

      it(`should fill the textarea`, () => {
        browser.setValue(textarea, sendText);
        assert.equal(browser.getValue(textarea), sendText, `textarea equals sent text`);
      });

    });
  });
});
<|endoftext|>