<commit_before>import '/resources/polymer/@polymer/polymer/polymer-legacy.js';
import '/resources/polymer/@polymer/iron-flex-layout/iron-flex-layout-classes.js';
import '/resources/polymer/@polymer/paper-icon-button/paper-icon-button.js';

import {_momentTz} from '/resources/reflection/tg-date-utils.js'

import {Polymer} from '/resources/polymer/@polymer/polymer/lib/legacy/polymer-fn.js';
import {html} from '/resources/polymer/@polymer/polymer/lib/utils/html-tag.js';

const template = html`

            <paper-icon-button icon="chevron-left" on-tap="_prevMonth"></paper-icon-button>

                    [[monthName]]  [[year]]

            <paper-icon-button icon="chevron-right" on-tap="_nextMonth"></paper-icon-button>

S

M

T

W

T

F

S

        <template is="dom-repeat" items="[[weeks]]" as="week">

                <template is="dom-repeat" items="[[week]]" as="day">

[[_absolute(day)]]

                </template>

        </template>

`;

(function () {
    const getDaysInMonth = function (year, month) {
        const yearMonth = { year: year, month: month, day: 1 };
        return _momentTz(yearMonth, this.timeZone).daysInMonth();
    };
    const getDaysInPreviousMonth = function (year, month) {
        const yearMonth = { year: year, month: month, day: 1 };
        return _momentTz(yearMonth, this.timeZone).add(-1, 'M').daysInMonth();
    };
    const createMonth = function (year, month) {
        const weeks = [];
        let week = [];
        let day = 1;
        const days = getDaysInMonth.bind(this)(year, month);
        while (day > 0) {
            week = createWeek.bind(this)(year, month, day);
            weeks.push(week);
            day = week[week.length - 1].day > 0 && week[week.length - 1].day < days ? week[week.length - 1].day + 1 : 0;
        }
        return weeks;
    };
    const createWeek = function (year, month, day) {
        const yearMonthDay = { year: year, month: month, day: day };
        const firstDay = _momentTz(yearMonthDay, this.timeZone).day();
        const week = [];
        const previousMonthDays = getDaysInPreviousMonth.bind(this)(year, month);
        let _i = 0;
        // In case when the first day of the month is not a sanday then add 0 to the first days of the week.
        for (; _i < firstDay; _i++) {
            week.push({monthIncrementor: -1, day: -1 * (previousMonthDays - (firstDay - (_i + 1)))});
        }
        // Add day numbers to the week.
        const days = getDaysInMonth.bind(this)(year, month);
        for (; _i < 7 && day <= days; _i++) {
            week.push({monthIncrementor: 0, day: day});
            day += 1;
        }
        // In case when the last day of the month is not saturday then add 0 to the last days of the week.
        let nextMonthDay = -1;
        for (; _i < 7; _i++) {
            week.push({monthIncrementor: 1, day: nextMonthDay});
            nextMonthDay -= 1;
        }
        return week;
    };
    Polymer({
        _template: template,

        is: "tg-month-selector",

        properties: {
            /**
             * If empty then default timezone should be used for toString and fromString conversions in 'moment()' and 'moment(...)' methods.
             * Otherwise -- the specified timezone should be used in 'moment.tz(timeZone)' and 'moment.tz(..., timeZone)' methods.
             */
            timeZone: {
                type: String
            },

            selectedDate: {
                type: Number,
                notify: true,
                observer: "_selectedDateChanged"
            },
            selectedDay: Number,
            selectedMonth: Number,
            selectedYear: Number,
            todayDay: Number,
            todayMonth: Number,
            todayYear: Number,
            monthName: String,
            month: Number,
            year: Number
        },

        ready: function () {
            var today = _momentTz(this.timeZone);
            this.todayDay = today.date();
            this.todayMonth = today.month();
            this.todayYear = today.year();
            this.selectedDay = null;
            this.selectedMonth = null;
            this.selectedYear = null;
            this.month = null;
            this.year = null;
        },

        attached: function () {
            var momentToShow = this.selectedDate ?
                    _momentTz(this.selectedDate, this.timeZone) :
                    _momentTz(this.timeZone);
            this._adjustMonth(momentToShow);
        },

        /**
         * Goes to the previous month.
         */
        _prevMonth: function () {
            this._selectMonth(-1);
        },

        /**
         * Goes to the next month.
         */
        _nextMonth: function () {
            this._selectMonth(1);
        },

        /**
         * Selects month depending on increment (the increment might be positive or negetive).
         */
        _selectMonth: function (inc) {
            var yearMonth = { year: this.year, month: this.month, day: 1 };
            var momentToShow = _momentTz(yearMonth, this.timeZone).add(inc, 'M');
            this._adjustMonth(momentToShow);
        },

        /**
         * Updates the calendar information depending on which month was choosen.
         */
        _adjustMonth: function (momentToShow) {
            if (momentToShow.isValid() && (this.month !== momentToShow.month() || this.year !== momentToShow.year())) {
                this.monthName = momentToShow.format("MMMM");
                this.month = momentToShow.month();
                this.year = momentToShow.year();
                this.weeks = createMonth.bind(this)(this.year, this.month);
            }
        },

        /**
         * Selects the date.
         */
        _selectDay: function (event, detail, el) {
            var yearMonthDay = { year: this.year, month: this.month + event.model.day.monthIncrementor, day: Math.abs(event.model.day.day) };
            this.selectedDate = _momentTz(yearMonthDay, this.timeZone).valueOf();
            if (event.detail.sourceEvent.detail > 1) {
                this.fire("tg-accept-date");
            }
        },

        /**
         * Listens when the selected date changes.
         */
        _selectedDateChanged: function (newValue, oldValue) {
            if (newValue !== null) {
                var newMoment = _momentTz(newValue, this.timeZone);
                if (newMoment.isValid()) {
                    this.selectedYear = newMoment.year();
                    this.selectedMonth = newMoment.month();
                    this._adjustMonth(newMoment);
                    this.selectedDay = newMoment.date();
                }
            }
        },

        /**
         * Calcualtes class names for specified day.
         */
        _calcDayClass: function (day, month, year, selectedDay, selectedMonth, selectedYear) {
            var dayClasses = "day layout vertical center-center";
            if (day.day < 0) {
                dayClasses += " outside-day";
            }
            if (day.day === this.todayDay && month === this.todayMonth && year === this.todayYear) {
                dayClasses += " today-date";
            }
            if (day.day === selectedDay && month === selectedMonth && year === selectedYear) {
                dayClasses += " selected-date";
            }
            return dayClasses;
        },

        /**
         * Returns the absolute value of the day
         */
        _absolute: function (day) {
            return Math.abs(day.day);
        }
    });
})();<commit_msg>#1469 Enabled date selection on fringes of the years.<commit_after>import '/resources/polymer/@polymer/polymer/polymer-legacy.js';
import '/resources/polymer/@polymer/iron-flex-layout/iron-flex-layout-classes.js';
import '/resources/polymer/@polymer/paper-icon-button/paper-icon-button.js';

import {_momentTz} from '/resources/reflection/tg-date-utils.js'

import {Polymer} from '/resources/polymer/@polymer/polymer/lib/legacy/polymer-fn.js';
import {html} from '/resources/polymer/@polymer/polymer/lib/utils/html-tag.js';

const template = html`

            <paper-icon-button icon="chevron-left" on-tap="_prevMonth"></paper-icon-button>

                    [[monthName]]  [[year]]

            <paper-icon-button icon="chevron-right" on-tap="_nextMonth"></paper-icon-button>

S

M

T

W

T

F

S

        <template is="dom-repeat" items="[[weeks]]" as="week">

                <template is="dom-repeat" items="[[week]]" as="day">

[[_absolute(day)]]

                </template>

        </template>

`;

(function () {
    const getDaysInMonth = function (year, month) {
        const yearMonth = { year: year, month: month, day: 1 };
        return _momentTz(yearMonth, this.timeZone).daysInMonth();
    };
    const getDaysInPreviousMonth = function (year, month) {
        const yearMonth = { year: year, month: month, day: 1 };
        return _momentTz(yearMonth, this.timeZone).add(-1, 'M').daysInMonth();
    };
    const createMonth = function (year, month) {
        const weeks = [];
        let week = [];
        let day = 1;
        const days = getDaysInMonth.bind(this)(year, month);
        while (day > 0) {
            week = createWeek.bind(this)(year, month, day);
            weeks.push(week);
            day = week[week.length - 1].day > 0 && week[week.length - 1].day < days ? week[week.length - 1].day + 1 : 0;
        }
        return weeks;
    };
    const createWeek = function (year, month, day) {
        const yearMonthDay = { year: year, month: month, day: day };
        const firstDay = _momentTz(yearMonthDay, this.timeZone).day();
        const week = [];
        const previousMonthDays = getDaysInPreviousMonth.bind(this)(year, month);
        let _i = 0;
        // In case when the first day of the month is not a sanday then add 0 to the first days of the week.
        for (; _i < firstDay; _i++) {
            week.push({monthIncrementor: -1, day: -1 * (previousMonthDays - (firstDay - (_i + 1)))});
        }
        // Add day numbers to the week.
        const days = getDaysInMonth.bind(this)(year, month);
        for (; _i < 7 && day <= days; _i++) {
            week.push({monthIncrementor: 0, day: day});
            day += 1;
        }
        // In case when the last day of the month is not saturday then add 0 to the last days of the week.
        let nextMonthDay = -1;
        for (; _i < 7; _i++) {
            week.push({monthIncrementor: 1, day: nextMonthDay});
            nextMonthDay -= 1;
        }
        return week;
    };
    Polymer({
        _template: template,

        is: "tg-month-selector",

        properties: {
            /**
             * If empty then default timezone should be used for toString and fromString conversions in 'moment()' and 'moment(...)' methods.
             * Otherwise -- the specified timezone should be used in 'moment.tz(timeZone)' and 'moment.tz(..., timeZone)' methods.
             */
            timeZone: {
                type: String
            },

            selectedDate: {
                type: Number,
                notify: true,
                observer: "_selectedDateChanged"
            },
            selectedDay: Number,
            selectedMonth: Number,
            selectedYear: Number,
            todayDay: Number,
            todayMonth: Number,
            todayYear: Number,
            monthName: String,
            month: Number,
            year: Number
        },

        ready: function () {
            var today = _momentTz(this.timeZone);
            this.todayDay = today.date();
            this.todayMonth = today.month();
            this.todayYear = today.year();
            this.selectedDay = null;
            this.selectedMonth = null;
            this.selectedYear = null;
            this.month = null;
            this.year = null;
        },

        attached: function () {
            var momentToShow = this.selectedDate ?
                    _momentTz(this.selectedDate, this.timeZone) :
                    _momentTz(this.timeZone);
            this._adjustMonth(momentToShow);
        },

        /**
         * Goes to the previous month.
         */
        _prevMonth: function () {
            this._selectMonth(-1);
        },

        /**
         * Goes to the next month.
         */
        _nextMonth: function () {
            this._selectMonth(1);
        },

        /**
         * Selects month depending on increment (the increment might be positive or negetive).
         */
        _selectMonth: function (inc) {
            var yearMonth = { year: this.year, month: this.month, day: 1 };
            var momentToShow = _momentTz(yearMonth, this.timeZone).add(inc, 'M');
            this._adjustMonth(momentToShow);
        },

        /**
         * Updates the calendar information depending on which month was choosen.
         */
        _adjustMonth: function (momentToShow) {
            if (momentToShow.isValid() && (this.month !== momentToShow.month() || this.year !== momentToShow.year())) {
                this.monthName = momentToShow.format("MMMM");
                this.month = momentToShow.month();
                this.year = momentToShow.year();
                this.weeks = createMonth.bind(this)(this.year, this.month);
            }
        },

        /**
         * Selects the date.
         */
        _selectDay: function (event, detail, el) {
            var yearMonthDay = { year: this.year, month: this.month, day: 1 };
            this.selectedDate = _momentTz(yearMonthDay, this.timeZone).add(event.model.day.monthIncrementor, 'M').date(Math.abs(event.model.day.day)).valueOf();
            if (event.detail.sourceEvent.detail > 1) {
                this.fire("tg-accept-date");
            }
        },

        /**
         * Listens when the selected date changes.
         */
        _selectedDateChanged: function (newValue, oldValue) {
            if (newValue !== null) {
                var newMoment = _momentTz(newValue, this.timeZone);
                if (newMoment.isValid()) {
                    this.selectedYear = newMoment.year();
                    this.selectedMonth = newMoment.month();
                    this._adjustMonth(newMoment);
                    this.selectedDay = newMoment.date();
                }
            }
        },

        /**
         * Calcualtes class names for specified day.
         */
        _calcDayClass: function (day, month, year, selectedDay, selectedMonth, selectedYear) {
            var dayClasses = "day layout vertical center-center";
            if (day.day < 0) {
                dayClasses += " outside-day";
            }
            if (day.day === this.todayDay && month === this.todayMonth && year === this.todayYear) {
                dayClasses += " today-date";
            }
            if (day.day === selectedDay && month === selectedMonth && year === selectedYear) {
                dayClasses += " selected-date";
            }
            return dayClasses;
        },

        /**
         * Returns the absolute value of the day
         */
        _absolute: function (day) {
            return Math.abs(day.day);
        }
    });
})();<|endoftext|>


<commit_before>require("babel-register");

var path = require('path');

function capabilities(caps) {
  if (typeof process.env.TRAVIS_BUILD_NUMBER !== 'undefined') {
    caps['build'] = process.env.TRAVIS_BUILD_NUMBER;
  };

  if(process.env.CONTINUOUS_INTEGRATION === 'true') {
    caps['name'] = 'wdio-screenshot integration test';
  } else {
    caps['name'] = 'wdio-screenshot development test';
  }

  return caps;
}

exports.config = {
  specs: [
    path.join(__dirname, '/specs/desktop.test.js')
  ],
  maxInstances: 4,
  capabilities: [
    capabilities({
      browserName: 'firefox',
      platform: 'Windows 7',
      screenResolution: '1920x1200',
    }),
    capabilities({
      browserName: 'chrome',
      platform: 'Windows 7',
      screenResolution: '1920x1200',
    }),
    capabilities({
      browserName: 'internet explorer',
      platform: 'Windows 7',
      version: '9.0',
      screenResolution: '1920x1200',
    }),
    capabilities({
      browserName: 'internet explorer',
      platform: 'Windows 7',
      version: '10.0',
      screenResolution: '1920x1200',
    }),
    capabilities({
      browserName: 'internet explorer',
      platform: 'Windows 7',
      version: '11.0',
      screenResolution: '1920x1200',
    }),
    capabilities({
      browserName: 'chrome',
      platform: 'OS X 10.11',
      screenResolution: '1920x1440',
    }),
    capabilities({
      browserName: 'firefox',
      platform: 'OS X 10.11',
      screenResolution: '1920x1440',
    }),
    capabilities({
      browserName: 'safari',
      platform: 'OS X 10.11',
      screenResolution: '1920x1440',
    })
  ],
  sync: false,
  logLevel: 'silent',
  coloredLogs: true,
  baseUrl: 'http://zinserjan.github.io/wdio-screenshot/integration',
  waitforTimeout: 30000,
  connectionRetryTimeout: 180000,
  connectionRetryCount: 3,

  framework: 'mocha',
  mochaOpts: {
    ui: 'bdd',
    timeout: 120000,
    compilers: [
      'js:babel-register'
    ],
  },
  before: function() {
    require('../../src').init(browser, {})
  },
  services: ['sauce'],
  user: process.env.SAUCE_USERNAME,
  key: process.env.SAUCE_ACCESS_KEY,
  sauceConnect: false,
}
<commit_msg>lock firefox version to latest stable. (#16)<commit_after>require("babel-register");

var path = require('path');

function capabilities(caps) {
  if (typeof process.env.TRAVIS_BUILD_NUMBER !== 'undefined') {
    caps['build'] = process.env.TRAVIS_BUILD_NUMBER;
  };

  if(process.env.CONTINUOUS_INTEGRATION === 'true') {
    caps['name'] = 'wdio-screenshot integration test';
  } else {
    caps['name'] = 'wdio-screenshot development test';
  }

  return caps;
}

exports.config = {
  specs: [
    path.join(__dirname, '/specs/desktop.test.js')
  ],
  maxInstances: 4,
  capabilities: [
    capabilities({
      browserName: 'firefox',
      platform: 'Windows 7',
      version: '46.0',
      screenResolution: '1920x1200',
    }),
    capabilities({
      browserName: 'chrome',
      platform: 'Windows 7',
      screenResolution: '1920x1200',
    }),
    capabilities({
      browserName: 'internet explorer',
      platform: 'Windows 7',
      version: '9.0',
      screenResolution: '1920x1200',
    }),
    capabilities({
      browserName: 'internet explorer',
      platform: 'Windows 7',
      version: '10.0',
      screenResolution: '1920x1200',
    }),
    capabilities({
      browserName: 'internet explorer',
      platform: 'Windows 7',
      version: '11.0',
      screenResolution: '1920x1200',
    }),
    capabilities({
      browserName: 'chrome',
      platform: 'OS X 10.11',
      screenResolution: '1920x1440',
    }),
    capabilities({
      browserName: 'firefox',
      platform: 'OS X 10.11',
      version: '46.0',
      screenResolution: '1920x1440',
    }),
    capabilities({
      browserName: 'safari',
      platform: 'OS X 10.11',
      screenResolution: '1920x1440',
    })
  ],
  sync: false,
  logLevel: 'silent',
  coloredLogs: true,
  baseUrl: 'http://zinserjan.github.io/wdio-screenshot/integration',
  waitforTimeout: 30000,
  connectionRetryTimeout: 180000,
  connectionRetryCount: 3,

  framework: 'mocha',
  mochaOpts: {
    ui: 'bdd',
    timeout: 120000,
    compilers: [
      'js:babel-register'
    ],
  },
  before: function() {
    require('../../src').init(browser, {})
  },
  services: ['sauce'],
  user: process.env.SAUCE_USERNAME,
  key: process.env.SAUCE_ACCESS_KEY,
  sauceConnect: false,
}
<|endoftext|>


<commit_before>/**
 * mSupply Mobile
 * Sustainable Solutions (NZ) Ltd. 2016
 */

import React from 'react';
import PropTypes from 'prop-types';
import {
  Dimensions,
  Keyboard,
  StyleSheet,
  Text,
  TouchableOpacity,
  View,
} from 'react-native';
import Icon from 'react-native-vector-icons/Ionicons';
import { Button } from 'react-native-ui-components';
import Modal from 'react-native-modalbox';
import globalStyles from '../../globalStyles';

export function ConfirmModal(props) {
  // On opening, dismiss the keyboard to ensure editable cells lose their focus
  // and their values become fixed (so that they save correctly)
  if (props.isOpen) Keyboard.dismiss();
  const { style, textStyle, onCancel, onConfirm, questionText, ...modalProps } = props;
  return (
    <Modal
      {...modalProps}
      style={[defaultStyles.modal, style]}
    >
      {onCancel && <TouchableOpacity onPress={onCancel} style={defaultStyles.closeButton}>
        <Icon name="md-close" style={defaultStyles.closeIcon} />
      </TouchableOpacity>}
      <View style={defaultStyles.contentContainer}>
        <Text style={textStyle}>
          {questionText}
        </Text>
        <View style={[defaultStyles.buttonContainer, props.buttonContainerStyle]}>
          {onCancel && <Button
            style={[globalStyles.button, props.cancelButtonStyle]}
            textStyle={[globalStyles.buttonText, props.buttonTextStyle]}
            text={props.cancelText}
            onPress={onCancel}
          />}
          {onConfirm && <Button
            style={[globalStyles.button, props.confirmButtonStyle]}
            textStyle={[globalStyles.buttonText, props.buttonTextStyle]}
            text={props.confirmText}
            onPress={onConfirm}
          />}
        </View>
      </View>
    </Modal>
   );
}

ConfirmModal.propTypes = {
  style: View.propTypes.style,
  buttonContainerStyle: View.propTypes.style,
  buttonTextStyle: Text.propTypes.style,
  cancelButtonStyle: View.propTypes.style,
  cancelText: PropTypes.string,
  confirmButtonStyle: View.propTypes.style,
  confirmText: PropTypes.string,
  textStyle: Text.propTypes.style,
  isOpen: PropTypes.bool.isRequired,
  questionText: PropTypes.string.isRequired,
  onCancel: PropTypes.func,
  onConfirm: PropTypes.func,
};
ConfirmModal.defaultProps = {
  cancelText: 'Cancel',
  confirmText: 'Confirm',
  style: {},
  globalStyles: {},
  swipeToClose: false, // negating the default.
  backdropPressToClose: false, // negating the default.
};

const defaultStyles = StyleSheet.create({
  buttonContainer: {
    flex: 1,
    flexDirection: 'row',
    paddingTop: 50,
  },
  contentContainer: {
    paddingTop: (Dimensions.get('window').height) / 3, // Start the content 33% down the page
  },
  closeButton: {
    position: 'absolute',
    top: 10,
    right: 20,
  },
  closeIcon: {
    fontSize: 36,
    color: 'white',
  },
});
<commit_msg>New button wrapper used in ConfirmModal<commit_after>/**
 * mSupply Mobile
 * Sustainable Solutions (NZ) Ltd. 2016
 */

import React from 'react';
import PropTypes from 'prop-types';
import {
  Dimensions,
  Keyboard,
  StyleSheet,
  Text,
  TouchableOpacity,
  View,
} from 'react-native';
import Icon from 'react-native-vector-icons/Ionicons';
import { OnePressButton } from '../';
import Modal from 'react-native-modalbox';
import globalStyles from '../../globalStyles';

export function ConfirmModal(props) {
  // On opening, dismiss the keyboard to ensure editable cells lose their focus
  // and their values become fixed (so that they save correctly)
  if (props.isOpen) Keyboard.dismiss();
  const { style, textStyle, onCancel, onConfirm, questionText, ...modalProps } = props;
  return (
    <Modal
      {...modalProps}
      style={[defaultStyles.modal, style]}
    >
      {onCancel && <TouchableOpacity onPress={onCancel} style={defaultStyles.closeButton}>
        <Icon name="md-close" style={defaultStyles.closeIcon} />
      </TouchableOpacity>}
      <View style={defaultStyles.contentContainer}>
        <Text style={textStyle}>
          {questionText}
        </Text>
        <View style={[defaultStyles.buttonContainer, props.buttonContainerStyle]}>
          {onCancel && <OnePressButton
            style={[globalStyles.button, props.cancelButtonStyle]}
            textStyle={[globalStyles.buttonText, props.buttonTextStyle]}
            text={props.cancelText}
            onPress={onCancel}
          />}
          {onConfirm && <OnePressButton
            style={[globalStyles.button, props.confirmButtonStyle]}
            textStyle={[globalStyles.buttonText, props.buttonTextStyle]}
            text={props.confirmText}
            onPress={onConfirm}
          />}
        </View>
      </View>
    </Modal>
   );
}

ConfirmModal.propTypes = {
  style: View.propTypes.style,
  buttonContainerStyle: View.propTypes.style,
  buttonTextStyle: Text.propTypes.style,
  cancelButtonStyle: View.propTypes.style,
  cancelText: PropTypes.string,
  confirmButtonStyle: View.propTypes.style,
  confirmText: PropTypes.string,
  textStyle: Text.propTypes.style,
  isOpen: PropTypes.bool.isRequired,
  questionText: PropTypes.string.isRequired,
  onCancel: PropTypes.func,
  onConfirm: PropTypes.func,
};
ConfirmModal.defaultProps = {
  cancelText: 'Cancel',
  confirmText: 'Confirm',
  style: {},
  globalStyles: {},
  swipeToClose: false, // negating the default.
  backdropPressToClose: false, // negating the default.
};

const defaultStyles = StyleSheet.create({
  buttonContainer: {
    flex: 1,
    flexDirection: 'row',
    paddingTop: 50,
  },
  contentContainer: {
    paddingTop: (Dimensions.get('window').height) / 3, // Start the content 33% down the page
  },
  closeButton: {
    position: 'absolute',
    top: 10,
    right: 20,
  },
  closeIcon: {
    fontSize: 36,
    color: 'white',
  },
});
<|endoftext|>


<commit_before>/* jslint mocha: true */
/*global describe, it, before, beforeEach, after, afterEach, $, Foundation */

describe('Abide', function() {
  var plugin;
  var $html;

  afterEach(function() {
    plugin.destroy();
    $html.remove();
  });

  describe('constructor()', function() {
    it('stores the element & plugin options', function() {
      $html = $('<form data-abide novalidate></form>').appendTo('body');
      plugin = new Foundation.Abide($html, {});

      plugin.$element.should.be.an('object');
      plugin.options.should.be.an('object');
    });

    it('the options are recursively merged', function() {
      $html = $('<form data-abide novalidate></form>').appendTo('body');

      var options = {
        validators: {
          notEqualTo: function (el, required, parent) {
            return $(`#${el.attr('data-equalto')}`).val() !== el.val();
          }
        }
      };

      plugin = new Foundation.Abide($html, options);

      plugin.options.validators.should.includes.keys('equalTo', 'notEqualTo');
    });
  });

  describe('validateInput()', function() {
    it('returns true for hidden inputs', function() {
      $html = $("<form data-abide novalidate><input type='hidden' required></form>").appendTo("body");
      plugin = new Foundation.Abide($html, {});

      plugin.validateInput($html.find("input")).should.equal(true);
    });

    it('returns true for inputs with [data-abide-ignore]', function() {
      $html = $("<form data-abide novalidate><input type='text' required data-abide-ignore></form>").appendTo("body");
      plugin = new Foundation.Abide($html, {});

      plugin.validateInput($html.find("input")).should.equal(true);
    });

    it('returns true for checked checkboxes', function() {
      $html = $("<form data-abide><input type='checkbox' required checked='checked'></form>").appendTo("body");
      plugin = new Foundation.Abide($html, {});

      plugin.validateInput($html.find("input")).should.equal(true);
    });

    it('returns false for unchecked checkboxes', function() {
      $html = $("<form data-abide><input type='checkbox' required></form>").appendTo("body");
      plugin = new Foundation.Abide($html, {});

      plugin.validateInput($html.find("input")).should.equal(false);
    });

    it('returns true for selected select option', function() {
      $html = $("<form data-abide><select required><option value=''></option><option value='One'>One</option><option value='Two' selected='selected'>Two</option></select></form>").appendTo("body");
      plugin = new Foundation.Abide($html, {});

      plugin.validateInput($html.find("select")).should.equal(true);
      $html.find("select").val().should.equal("Two");
    });

    it('returns false for unselected select option', function() {
      $html = $("<form data-abide><select required><option value=''></option><option value='One'>One</option><option value='Two'>Two</option></select></form>").appendTo("body");
      plugin = new Foundation.Abide($html, {});

      plugin.validateInput($html.find("select")).should.equal(false);
    });
  });

  describe('addErrorClasses()', function() {
    it('adds aria-invalid attribute to element', function() {
      $html = $('<form data-abide><input type="text"></form>').appendTo('body');
      plugin = new Foundation.Abide($html, {});

      plugin.addErrorClasses($html.find('input'));

      $html.find('input').should.have.attr('aria-invalid', 'true')
    });
  });

  describe('addGlobalErrorA11yAttributes()', function () {
    it('adds [aria-live] attribute on element', function () {
      $html = $(`<form data-abide><span data-abide-error></span></form>`).appendTo('body');
      plugin = new Foundation.Abide($html, { a11yErrorLevel: 'test-level' });
      plugin.addA11yAttributes($html.find('[data-abide-error]'));

      $html.find('[data-abide-error]').should.have.attr('aria-live', 'test-level');
    });
  });

  describe('addA11yAttributes()', function () {
    it('adds [aria-describedby] attribute to field and [for] attribute to form error', function() {
      $html = $(`
        <form data-abide>
          <input type="text" id="test-input">
          Form error
        </form>
      `).appendTo('body');
      plugin = new Foundation.Abide($html, {});
      plugin.addA11yAttributes($html.find('input'));

      $html.find('input').should.have.attr('aria-describedby', 'test-error');
      $html.find('label.form-error').should.have.attr('for', 'test-input');
    });

    it('adds attributes and ids when no id is set', function() {
      $html = $(`
        <form data-abide>
          <input type="text">
          Form error
        </form>
      `).appendTo('body');
      plugin = new Foundation.Abide($html, {});
      plugin.addA11yAttributes($html.find('input'));

      const errorId = $html.find('.form-error').attr('id');
      $html.find('.form-error').should.have.attr('id').exist;
      $html.find('input').should.have.attr('aria-describedby', errorId);

      const inputId = $html.find('input').attr('id');
      $html.find('input').should.have.attr('id').exist;
      $html.find('.form-error').should.have.attr('for', inputId);
    });
  });

  describe('removeErrorClasses()', function() {
    it('removes aria-invalid attribute from element', function() {
      $html = $('<form data-abide><input type="text"></form>').appendTo('body');
      plugin = new Foundation.Abide($html, {});
      // Add error classes first
      plugin.addErrorClasses($html.find('input'));

      plugin.removeErrorClasses($html.find('input'));

      $html.find('input').should.not.have.attr('aria-invalid')
    });
  });

  describe('removeRadioErrorClasses()', function() {
    it('removes aria-invalid attribute from radio group', function() {
      $html = $('<form data-abide><input type="radio" name="groupName"></form>').appendTo('body');
      plugin = new Foundation.Abide($html, {});
      // Add error classes first
      plugin.addErrorClasses($html.find('input'));

      plugin.removeRadioErrorClasses('groupName');

      $html.find('input').should.not.have.attr('aria-invalid')
    });
  });

  describe('resetForm()', function() {
    it('removes aria-invalid attribute from elements', function() {
      $html = $('<form data-abide><input type="text"></form>').appendTo('body');
      plugin = new Foundation.Abide($html, {});
      // Add error classes first
      plugin.addErrorClasses($html.find('input'));

      plugin.resetForm();

      $html.find('input').should.not.have.attr('aria-invalid')
    });
  });
});
<commit_msg>test: add abide unit tests for validateForm<commit_after>/* jslint mocha: true */
/*global describe, it, before, beforeEach, after, afterEach, $, Foundation */

describe('Abide', function() {
  var plugin;
  var $html;

  afterEach(function() {
    plugin.destroy();
    $html.remove();
  });

  describe('constructor()', function() {
    it('stores the element & plugin options', function() {
      $html = $('<form data-abide novalidate></form>').appendTo('body');
      plugin = new Foundation.Abide($html, {});

      plugin.$element.should.be.an('object');
      plugin.options.should.be.an('object');
    });

    it('the options are recursively merged', function() {
      $html = $('<form data-abide novalidate></form>').appendTo('body');

      var options = {
        validators: {
          notEqualTo: function (el, required, parent) {
            return $(`#${el.attr('data-equalto')}`).val() !== el.val();
          }
        }
      };

      plugin = new Foundation.Abide($html, options);

      plugin.options.validators.should.includes.keys('equalTo', 'notEqualTo');
    });
  });

  describe('validateForm()', function() {
    it('returns true after validation is disabled', function() {
      $html = $("<form data-abide novalidate><input required type='text'><input type='submit'></form>").appendTo("body");
      plugin = new Foundation.Abide($html, {});

      plugin.disableValidation();
      plugin.validateForm().should.equal(true);
    });

    it('returns false after validation is (re)enabled', function() {
      $html = $("<form data-abide novalidate><input required type='text'><input type='submit'></form>").appendTo("body");
      plugin = new Foundation.Abide($html, {});

      plugin.disableValidation();
      plugin.enableValidation();
      plugin.validateForm().should.equal(false);
    });

    it('returns true if first submit has formnovalidate is disabled', function() {
      $html = $("<form data-abide novalidate><input required type='text'><input type='submit' formnovalidate></form>").appendTo("body");
      plugin = new Foundation.Abide($html, {});

      plugin.validateForm().should.equal(true);
    });
  });

  describe('validateInput()', function() {
    it('returns true for hidden inputs', function() {
      $html = $("<form data-abide novalidate><input type='hidden' required></form>").appendTo("body");
      plugin = new Foundation.Abide($html, {});

      plugin.validateInput($html.find("input")).should.equal(true);
    });

    it('returns true for inputs with [data-abide-ignore]', function() {
      $html = $("<form data-abide novalidate><input type='text' required data-abide-ignore></form>").appendTo("body");
      plugin = new Foundation.Abide($html, {});

      plugin.validateInput($html.find("input")).should.equal(true);
    });

    it('returns true for checked checkboxes', function() {
      $html = $("<form data-abide><input type='checkbox' required checked='checked'></form>").appendTo("body");
      plugin = new Foundation.Abide($html, {});

      plugin.validateInput($html.find("input")).should.equal(true);
    });

    it('returns false for unchecked checkboxes', function() {
      $html = $("<form data-abide><input type='checkbox' required></form>").appendTo("body");
      plugin = new Foundation.Abide($html, {});

      plugin.validateInput($html.find("input")).should.equal(false);
    });

    it('returns true for selected select option', function() {
      $html = $("<form data-abide><select required><option value=''></option><option value='One'>One</option><option value='Two' selected='selected'>Two</option></select></form>").appendTo("body");
      plugin = new Foundation.Abide($html, {});

      plugin.validateInput($html.find("select")).should.equal(true);
      $html.find("select").val().should.equal("Two");
    });

    it('returns false for unselected select option', function() {
      $html = $("<form data-abide><select required><option value=''></option><option value='One'>One</option><option value='Two'>Two</option></select></form>").appendTo("body");
      plugin = new Foundation.Abide($html, {});

      plugin.validateInput($html.find("select")).should.equal(false);
    });
  });

  describe('addErrorClasses()', function() {
    it('adds aria-invalid attribute to element', function() {
      $html = $('<form data-abide><input type="text"></form>').appendTo('body');
      plugin = new Foundation.Abide($html, {});

      plugin.addErrorClasses($html.find('input'));

      $html.find('input').should.have.attr('aria-invalid', 'true')
    });
  });

  describe('addGlobalErrorA11yAttributes()', function () {
    it('adds [aria-live] attribute on element', function () {
      $html = $(`<form data-abide><span data-abide-error></span></form>`).appendTo('body');
      plugin = new Foundation.Abide($html, { a11yErrorLevel: 'test-level' });
      plugin.addA11yAttributes($html.find('[data-abide-error]'));

      $html.find('[data-abide-error]').should.have.attr('aria-live', 'test-level');
    });
  });

  describe('addA11yAttributes()', function () {
    it('adds [aria-describedby] attribute to field and [for] attribute to form error', function() {
      $html = $(`
        <form data-abide>
          <input type="text" id="test-input">
          Form error
        </form>
      `).appendTo('body');
      plugin = new Foundation.Abide($html, {});
      plugin.addA11yAttributes($html.find('input'));

      $html.find('input').should.have.attr('aria-describedby', 'test-error');
      $html.find('label.form-error').should.have.attr('for', 'test-input');
    });

    it('adds attributes and ids when no id is set', function() {
      $html = $(`
        <form data-abide>
          <input type="text">
          Form error
        </form>
      `).appendTo('body');
      plugin = new Foundation.Abide($html, {});
      plugin.addA11yAttributes($html.find('input'));

      const errorId = $html.find('.form-error').attr('id');
      $html.find('.form-error').should.have.attr('id').exist;
      $html.find('input').should.have.attr('aria-describedby', errorId);

      const inputId = $html.find('input').attr('id');
      $html.find('input').should.have.attr('id').exist;
      $html.find('.form-error').should.have.attr('for', inputId);
    });
  });

  describe('removeErrorClasses()', function() {
    it('removes aria-invalid attribute from element', function() {
      $html = $('<form data-abide><input type="text"></form>').appendTo('body');
      plugin = new Foundation.Abide($html, {});
      // Add error classes first
      plugin.addErrorClasses($html.find('input'));

      plugin.removeErrorClasses($html.find('input'));

      $html.find('input').should.not.have.attr('aria-invalid')
    });
  });

  describe('removeRadioErrorClasses()', function() {
    it('removes aria-invalid attribute from radio group', function() {
      $html = $('<form data-abide><input type="radio" name="groupName"></form>').appendTo('body');
      plugin = new Foundation.Abide($html, {});
      // Add error classes first
      plugin.addErrorClasses($html.find('input'));

      plugin.removeRadioErrorClasses('groupName');

      $html.find('input').should.not.have.attr('aria-invalid')
    });
  });

  describe('resetForm()', function() {
    it('removes aria-invalid attribute from elements', function() {
      $html = $('<form data-abide><input type="text"></form>').appendTo('body');
      plugin = new Foundation.Abide($html, {});
      // Add error classes first
      plugin.addErrorClasses($html.find('input'));

      plugin.resetForm();

      $html.find('input').should.not.have.attr('aria-invalid')
    });
  });
});
<|endoftext|>


<commit_before>module('Http method element');
test('Should be uppercase', function (){
	equal(new HttpMethodElement('get').get(), 'GET',
		'A lowercase GET http method should be uppercase');
	equal(new HttpMethodElement('pOsT').get(), 'POST',
		'A mixed case POST http method should be uppercase');
});
test('Should handle non-values', function (){
	equal(new HttpMethodElement().get(), '',
		'An undefined http method should be normalized to an empty string');
	equal(new HttpMethodElement('').get(), '',
		'An empty http method should be normalized to an empty string');
	equal(new HttpMethodElement(null).get(), '',
		'A null http method should be normalized to an empty string');
});

module('Url element')
test('Should be normalized', function () {
	equal(new UrlElement('http://example.co.uk').get(), 'http://example.co.uk',
		'A valid url should remain the same');
	equal(new UrlElement('http://EXAMPLE.co.UK/endpoint').get(), 'http://example.co.uk/endpoint',
		'The url authority must be lowercase');
	equal(new UrlElement('http://EXAMPLE.co.UK/endpoint/').get(), 'http://example.co.uk/endpoint/',
		'It should not strip off the trailing /');
	equal(new UrlElement('HTTP://example.org').get(), 'http://example.org',
		'The url scheme must be lowercase');
	equal(new UrlElement('http://example.org:80').get(), 'http://example.org',
		'The default http port (80) MUST be excluded');
	equal(new UrlElement('https://example.org:443').get(), 'https://example.org',
		'The default https port (443) MUST be excluded');
	equal(new UrlElement('http://example.org:8080').get(), 'http://example.org:8080',
		'The non default http port MUST be included');
	equal(new UrlElement('https://example.org:8080').get(), 'https://example.org:8080',
		'The non default https port MUST be included');
	equal(new UrlElement('http://example.org/?foo=bar').get(), 'http://example.org/',
		'The query string should not be included');
	equal(new UrlElement('http://example.org/#anchor').get(), 'http://example.org/',
		'The anchor should not be included');
	equal(new UrlElement('example.org').get(), 'http://example.org',
		'The http url scheme is added automatically');
	equal(new UrlElement('example.org:100').get(), 'http://example.org:100',
		'The port will not be stripped if the scheme is missing');
	equal(new UrlElement('example.org:80').get(), 'http://example.org',
		'The default http port will be stripped if the scheme is missing');
});
test('Should handle non-values', function () {
	equal(new UrlElement().get(), '',
		'An undefined url should be normalized to an empty string');
	equal(new UrlElement('').get(), '',
		'An empty url should be normalized to an empty string');
	equal(new UrlElement(null).get(), '',
		'An null url should be normalized to an empty string');
});

module('OAuth Signature Base String');
test('It should start with an uppercase http method, followed by two ampersands', function () {
	equal(new SignatureBaseString('get').generate(), 'GET&&',
		'The element separator (&) should be included for omitted elements');
	equal(new SignatureBaseString('pOsT').generate(), 'POST&&',
		'The element separator (&) should be included for omitted elements');
	equal(new SignatureBaseString().generate(), '&&',
		'The http method shouldn\'t be included if it is undefined');
	equal(new SignatureBaseString('').generate(), '&&',
		'The http method shouldn\'t be included if it is empty');
	equal(new SignatureBaseString(null).generate(), '&&',
		'The http method shouldn\'t be included if it is null');
});
test('The url should be included in the second element after the http method and should be suffixed by an ampersand', function () {
	equal(new SignatureBaseString('GET', 'http://example.co.uk').generate(), 'GET&http://example.co.uk&',
		'The http method should be the first component of the url');
	equal(new SignatureBaseString('', 'http://EXAMPLE.co.UK/endpoint').generate(), '&http://example.co.uk/endpoint&',
		'The url authority must be lowercase');
    equal(new SignatureBaseString('', 'http://EXAMPLE.co.UK/endpoint/').generate(), '&http://example.co.uk/endpoint/&',
		'It should not strip off the trailing /');
    equal(new SignatureBaseString('', 'HTTP://example.org').generate(), '&http://example.org&',
        'The url scheme must be lowercase');
    equal(new SignatureBaseString('', 'http://example.org:80').generate(), '&http://example.org&',
		'The default http port (80) MUST be excluded');
    equal(new SignatureBaseString('', 'https://example.org:443').generate(), '&https://example.org&',
        'The default https port (443) MUST be excluded');
    equal(new SignatureBaseString('', 'http://example.org:8080').generate(), '&http://example.org:8080&',
        'The non default http port MUST be included');
    equal(new SignatureBaseString('', 'https://example.org:8080').generate(), '&https://example.org:8080&',
        'The non default https port MUST be included');
    equal(new SignatureBaseString('GET', 'http://example.org/?foo=bar').generate(), 'GET&http://example.org/&',
        'The query string should not be included');
    equal(new SignatureBaseString('GET', 'http://example.org/#anchor').generate(), 'GET&http://example.org/&',
        'The anchor should not be included');
	equal(new SignatureBaseString('', '').generate(), '&&',
		'The resource url should not be included if it is empty');
	equal(new SignatureBaseString('', null).generate(), '&&',
		'The resource url should not be included if it is null');
	equal(new SignatureBaseString('', 'example.org').generate(), '&http://example.org&',
		'The http url scheme is added automatically');
	equal(new SignatureBaseString('', 'example.org:100').generate(), '&http://example.org:100&',
		'The port will not be stripped if the scheme is missing');
	equal(new SignatureBaseString('', 'example.org:80').generate(), '&http://example.org&',
		'The default http port will be stripped if the scheme is missing');
});
test('The normalized request parameters should be the last element', function () {
	equal(new SignatureBaseString('', '', { foo : 'bar' }).generate(), '&&foo=bar',
		'The parameter should be appended');
	equal(new SignatureBaseString('', '', { foo : 'bar', baz : 'qux' }).generate(), '&&baz=qux&foo=bar',
		'The parameters specified with object initializer should be ordered alphabetically');
	equal(new SignatureBaseString('', '', [{ foo : 'bar' }, { baz : 'qux' }]).generate(), '&&baz=qux&foo=bar',
		'The parameter specified with an array of objects should be ordered alphabetically');
	equal(new SignatureBaseString('', '', [{ foo : 'qux' }, { foo : 'bar'}, {foo : 'baz' }, { a : 'b' }]).generate(), '&&a=b&foo=bar&foo=baz&foo=qux',
		'The parameter specified with an array of objects with the same key should be ordered alphabetically by value');
	equal(new SignatureBaseString('', '', [{ foo : [ 'qux', 'bar', 'baz' ]}, { a : 'b' }]).generate(), '&&a=b&foo=bar&foo=baz&foo=qux',
		'The parameter specified with an array of objects with an array of values for the same key should be ordered alphabetically by value');
	equal(new SignatureBaseString('', '', { foo : [ 'qux', 'bar', 'baz'], a : 'b' }).generate(), '&&a=b&foo=bar&foo=baz&foo=qux',
		'The array of values for a single key should be ordered alphabetically by value');
	equal(new SignatureBaseString('', '', '').generate(), '&&',
		'The request parameters should not be included if it is empty');
	equal(new SignatureBaseString('', '', null).generate(), '&&',
		'The request parameters should not be included if it is null');
	equal(new SignatureBaseString('', '', [{ z : 't' }, { z : 'p'}, { f : 'a' }, { f : '50' }, { f : '25' }, { c : 'hi there' }, { a : 1 }]).generate(), '&&a=1&c=hi%20there&f=25&f=50&f=a&z=p&z=t',
		'The parameter specified with an array of objects with the same key should be ordered alphabetically by value');
});

module('OAuth Encoding');
test('The value should be encoded following the RFC3986', function () {
	var i,
		unreservedCharacters =  'ABCDEFGHIJKLMNOPQRSTUVWXYZ' +
							    'abcdefghijklmnopqrstuvwxyz' +
								'0123456789-_.~',
		reservedCharactersWithEncoding =[
			['!', '%21'], ['#', '%23'], ['$', '%24'], ['&', '%26'],	['\'', '%27'],
			['(', '%28'], [')', '%29'], ['*', '%2A'], ['+', '%2B'], [',', '%2C'],
			['/', '%2F'], [':', '%3A'], [';', '%3B'], ['=', '%3D'], ['?', '%3F'],
			['@', '%40'], ['[', '%5B'], [']', '%5D']
		];
	equal(new Rfc3986().encode(unreservedCharacters), unreservedCharacters,
		'Characters in the unreserved character set MUST NOT be encoded');
	equal(new Rfc3986().encode('*'), '%2A',
		'Hexadecimal characters in the encodings MUST be uppercase using the percent-encoding (%xx)');
	for (i = 0; i < reservedCharactersWithEncoding.length; i++) {
		equal(new Rfc3986().encode(reservedCharactersWithEncoding[i][0]), reservedCharactersWithEncoding[i][1],
			'Characters not in the unreserved character set MUST be encoded');

	}
	equal(new Rfc3986().encode('%'), '%25',
		'Percent character must be encoded');
	equal(new Rfc3986().encode(), '',
		'Undefined value should return empty string');
	equal(new Rfc3986().encode(''), '',
		'Empty value should return empty string');
	equal(new Rfc3986().encode(null), '',
		'Null value should return empty string');
});
test('The value containing UTF8 characters should be encoded following the RFC3629', function () {
	equal(new Rfc3986().encode('åçñ'), '%C3%A5%C3%A7%C3%B1',
		'Text names and values MUST be encoded as UTF-8 octets before percent-encoding them');
	equal(new Rfc3986().encode('你好'), '%E4%BD%A0%E5%A5%BD',
		'Text names and values MUST be encoded as UTF-8 octets before percent-encoding them');

});
test('The value should be decoded following the RFC3986', function () {
	var i,
		unreservedCharacters =  'ABCDEFGHIJKLMNOPQRSTUVWXYZ' +
			'abcdefghijklmnopqrstuvwxyz' +
			'0123456789-_.~',
		reservedCharactersWithEncoding =[
			['!', '%21'], ['#', '%23'], ['$', '%24'], ['&', '%26'],	['\'', '%27'],
			['(', '%28'], [')', '%29'], ['*', '%2A'], ['+', '%2B'], [',', '%2C'],
			['/', '%2F'], [':', '%3A'], [';', '%3B'], ['=', '%3D'], ['?', '%3F'],
			['@', '%40'], ['[', '%5B'], [']', '%5D']
		];
	equal(new Rfc3986().decode(unreservedCharacters), unreservedCharacters,
		'Not encoded characters in the unreserved character set MUST NOT be decoded');
	for (i = 0; i < reservedCharactersWithEncoding.length; i++) {
		equal(new Rfc3986().decode(reservedCharactersWithEncoding[i][1]), reservedCharactersWithEncoding[i][0],
			'Encoded characters not in the unreserved character set MUST be decoded');

	}
	equal(new Rfc3986().decode('%25'), '%',
		'Encoded percent character must be decoded');
	equal(new Rfc3986().decode('%31%32%33%41%42%43'), '123ABC',
		'Encoded unreserved characters must be decoded');
	equal(new Rfc3986().decode(), '',
		'Undefined value should return empty string');
	equal(new Rfc3986().decode(''), '',
		'Empty value should return empty string');
	equal(new Rfc3986().decode(null), '',
		'Null value should return empty string');
});
test('The value containing encoded UTF8 characters should be decoded following the RFC3629', function () {
	equal(new Rfc3986().decode('%C3%A5%C3%A7%C3%B1'), 'åçñ',
		'Text names and values MUST be encoded as UTF-8 octets before percent-encoding them');
	equal(new Rfc3986().decode('%E4%BD%A0%E5%A5%BD'), '你好',
		'Text names and values MUST be encoded as UTF-8 octets before percent-encoding them');
});<commit_msg>Made naming of test modules consistent with objects<commit_after>module('HttpMethodElement');
test('Should be uppercase', function (){
	equal(new HttpMethodElement('get').get(), 'GET',
		'A lowercase GET http method should be uppercase');
	equal(new HttpMethodElement('pOsT').get(), 'POST',
		'A mixed case POST http method should be uppercase');
});
test('Should handle non-values', function (){
	equal(new HttpMethodElement().get(), '',
		'An undefined http method should be normalized to an empty string');
	equal(new HttpMethodElement('').get(), '',
		'An empty http method should be normalized to an empty string');
	equal(new HttpMethodElement(null).get(), '',
		'A null http method should be normalized to an empty string');
});

module('UrlElement')
test('Should be normalized', function () {
	equal(new UrlElement('http://example.co.uk').get(), 'http://example.co.uk',
		'A valid url should remain the same');
	equal(new UrlElement('http://EXAMPLE.co.UK/endpoint').get(), 'http://example.co.uk/endpoint',
		'The url authority must be lowercase');
	equal(new UrlElement('http://EXAMPLE.co.UK/endpoint/').get(), 'http://example.co.uk/endpoint/',
		'It should not strip off the trailing /');
	equal(new UrlElement('HTTP://example.org').get(), 'http://example.org',
		'The url scheme must be lowercase');
	equal(new UrlElement('http://example.org:80').get(), 'http://example.org',
		'The default http port (80) MUST be excluded');
	equal(new UrlElement('https://example.org:443').get(), 'https://example.org',
		'The default https port (443) MUST be excluded');
	equal(new UrlElement('http://example.org:8080').get(), 'http://example.org:8080',
		'The non default http port MUST be included');
	equal(new UrlElement('https://example.org:8080').get(), 'https://example.org:8080',
		'The non default https port MUST be included');
	equal(new UrlElement('http://example.org/?foo=bar').get(), 'http://example.org/',
		'The query string should not be included');
	equal(new UrlElement('http://example.org/#anchor').get(), 'http://example.org/',
		'The anchor should not be included');
	equal(new UrlElement('example.org').get(), 'http://example.org',
		'The http url scheme is added automatically');
	equal(new UrlElement('example.org:100').get(), 'http://example.org:100',
		'The port will not be stripped if the scheme is missing');
	equal(new UrlElement('example.org:80').get(), 'http://example.org',
		'The default http port will be stripped if the scheme is missing');
});
test('Should handle non-values', function () {
	equal(new UrlElement().get(), '',
		'An undefined url should be normalized to an empty string');
	equal(new UrlElement('').get(), '',
		'An empty url should be normalized to an empty string');
	equal(new UrlElement(null).get(), '',
		'An null url should be normalized to an empty string');
});

module('OAuth Signature Base String');
test('It should start with an uppercase http method, followed by two ampersands', function () {
	equal(new SignatureBaseString('get').generate(), 'GET&&',
		'The element separator (&) should be included for omitted elements');
	equal(new SignatureBaseString('pOsT').generate(), 'POST&&',
		'The element separator (&) should be included for omitted elements');
	equal(new SignatureBaseString().generate(), '&&',
		'The http method shouldn\'t be included if it is undefined');
	equal(new SignatureBaseString('').generate(), '&&',
		'The http method shouldn\'t be included if it is empty');
	equal(new SignatureBaseString(null).generate(), '&&',
		'The http method shouldn\'t be included if it is null');
});
test('The url should be included in the second element after the http method and should be suffixed by an ampersand', function () {
	equal(new SignatureBaseString('GET', 'http://example.co.uk').generate(), 'GET&http://example.co.uk&',
		'The http method should be the first component of the url');
	equal(new SignatureBaseString('', 'http://EXAMPLE.co.UK/endpoint').generate(), '&http://example.co.uk/endpoint&',
		'The url authority must be lowercase');
    equal(new SignatureBaseString('', 'http://EXAMPLE.co.UK/endpoint/').generate(), '&http://example.co.uk/endpoint/&',
		'It should not strip off the trailing /');
    equal(new SignatureBaseString('', 'HTTP://example.org').generate(), '&http://example.org&',
        'The url scheme must be lowercase');
    equal(new SignatureBaseString('', 'http://example.org:80').generate(), '&http://example.org&',
		'The default http port (80) MUST be excluded');
    equal(new SignatureBaseString('', 'https://example.org:443').generate(), '&https://example.org&',
        'The default https port (443) MUST be excluded');
    equal(new SignatureBaseString('', 'http://example.org:8080').generate(), '&http://example.org:8080&',
        'The non default http port MUST be included');
    equal(new SignatureBaseString('', 'https://example.org:8080').generate(), '&https://example.org:8080&',
        'The non default https port MUST be included');
    equal(new SignatureBaseString('GET', 'http://example.org/?foo=bar').generate(), 'GET&http://example.org/&',
        'The query string should not be included');
    equal(new SignatureBaseString('GET', 'http://example.org/#anchor').generate(), 'GET&http://example.org/&',
        'The anchor should not be included');
	equal(new SignatureBaseString('', '').generate(), '&&',
		'The resource url should not be included if it is empty');
	equal(new SignatureBaseString('', null).generate(), '&&',
		'The resource url should not be included if it is null');
	equal(new SignatureBaseString('', 'example.org').generate(), '&http://example.org&',
		'The http url scheme is added automatically');
	equal(new SignatureBaseString('', 'example.org:100').generate(), '&http://example.org:100&',
		'The port will not be stripped if the scheme is missing');
	equal(new SignatureBaseString('', 'example.org:80').generate(), '&http://example.org&',
		'The default http port will be stripped if the scheme is missing');
});
test('The normalized request parameters should be the last element', function () {
	equal(new SignatureBaseString('', '', { foo : 'bar' }).generate(), '&&foo=bar',
		'The parameter should be appended');
	equal(new SignatureBaseString('', '', { foo : 'bar', baz : 'qux' }).generate(), '&&baz=qux&foo=bar',
		'The parameters specified with object initializer should be ordered alphabetically');
	equal(new SignatureBaseString('', '', [{ foo : 'bar' }, { baz : 'qux' }]).generate(), '&&baz=qux&foo=bar',
		'The parameter specified with an array of objects should be ordered alphabetically');
	equal(new SignatureBaseString('', '', [{ foo : 'qux' }, { foo : 'bar'}, {foo : 'baz' }, { a : 'b' }]).generate(), '&&a=b&foo=bar&foo=baz&foo=qux',
		'The parameter specified with an array of objects with the same key should be ordered alphabetically by value');
	equal(new SignatureBaseString('', '', [{ foo : [ 'qux', 'bar', 'baz' ]}, { a : 'b' }]).generate(), '&&a=b&foo=bar&foo=baz&foo=qux',
		'The parameter specified with an array of objects with an array of values for the same key should be ordered alphabetically by value');
	equal(new SignatureBaseString('', '', { foo : [ 'qux', 'bar', 'baz'], a : 'b' }).generate(), '&&a=b&foo=bar&foo=baz&foo=qux',
		'The array of values for a single key should be ordered alphabetically by value');
	equal(new SignatureBaseString('', '', '').generate(), '&&',
		'The request parameters should not be included if it is empty');
	equal(new SignatureBaseString('', '', null).generate(), '&&',
		'The request parameters should not be included if it is null');
	equal(new SignatureBaseString('', '', [{ z : 't' }, { z : 'p'}, { f : 'a' }, { f : '50' }, { f : '25' }, { c : 'hi there' }, { a : 1 }]).generate(), '&&a=1&c=hi%20there&f=25&f=50&f=a&z=p&z=t',
		'The parameter specified with an array of objects with the same key should be ordered alphabetically by value');
});

module('OAuth Encoding');
test('The value should be encoded following the RFC3986', function () {
	var i,
		unreservedCharacters =  'ABCDEFGHIJKLMNOPQRSTUVWXYZ' +
							    'abcdefghijklmnopqrstuvwxyz' +
								'0123456789-_.~',
		reservedCharactersWithEncoding =[
			['!', '%21'], ['#', '%23'], ['$', '%24'], ['&', '%26'],	['\'', '%27'],
			['(', '%28'], [')', '%29'], ['*', '%2A'], ['+', '%2B'], [',', '%2C'],
			['/', '%2F'], [':', '%3A'], [';', '%3B'], ['=', '%3D'], ['?', '%3F'],
			['@', '%40'], ['[', '%5B'], [']', '%5D']
		];
	equal(new Rfc3986().encode(unreservedCharacters), unreservedCharacters,
		'Characters in the unreserved character set MUST NOT be encoded');
	equal(new Rfc3986().encode('*'), '%2A',
		'Hexadecimal characters in the encodings MUST be uppercase using the percent-encoding (%xx)');
	for (i = 0; i < reservedCharactersWithEncoding.length; i++) {
		equal(new Rfc3986().encode(reservedCharactersWithEncoding[i][0]), reservedCharactersWithEncoding[i][1],
			'Characters not in the unreserved character set MUST be encoded');

	}
	equal(new Rfc3986().encode('%'), '%25',
		'Percent character must be encoded');
	equal(new Rfc3986().encode(), '',
		'Undefined value should return empty string');
	equal(new Rfc3986().encode(''), '',
		'Empty value should return empty string');
	equal(new Rfc3986().encode(null), '',
		'Null value should return empty string');
});
test('The value containing UTF8 characters should be encoded following the RFC3629', function () {
	equal(new Rfc3986().encode('åçñ'), '%C3%A5%C3%A7%C3%B1',
		'Text names and values MUST be encoded as UTF-8 octets before percent-encoding them');
	equal(new Rfc3986().encode('你好'), '%E4%BD%A0%E5%A5%BD',
		'Text names and values MUST be encoded as UTF-8 octets before percent-encoding them');

});
test('The value should be decoded following the RFC3986', function () {
	var i,
		unreservedCharacters =  'ABCDEFGHIJKLMNOPQRSTUVWXYZ' +
			'abcdefghijklmnopqrstuvwxyz' +
			'0123456789-_.~',
		reservedCharactersWithEncoding =[
			['!', '%21'], ['#', '%23'], ['$', '%24'], ['&', '%26'],	['\'', '%27'],
			['(', '%28'], [')', '%29'], ['*', '%2A'], ['+', '%2B'], [',', '%2C'],
			['/', '%2F'], [':', '%3A'], [';', '%3B'], ['=', '%3D'], ['?', '%3F'],
			['@', '%40'], ['[', '%5B'], [']', '%5D']
		];
	equal(new Rfc3986().decode(unreservedCharacters), unreservedCharacters,
		'Not encoded characters in the unreserved character set MUST NOT be decoded');
	for (i = 0; i < reservedCharactersWithEncoding.length; i++) {
		equal(new Rfc3986().decode(reservedCharactersWithEncoding[i][1]), reservedCharactersWithEncoding[i][0],
			'Encoded characters not in the unreserved character set MUST be decoded');

	}
	equal(new Rfc3986().decode('%25'), '%',
		'Encoded percent character must be decoded');
	equal(new Rfc3986().decode('%31%32%33%41%42%43'), '123ABC',
		'Encoded unreserved characters must be decoded');
	equal(new Rfc3986().decode(), '',
		'Undefined value should return empty string');
	equal(new Rfc3986().decode(''), '',
		'Empty value should return empty string');
	equal(new Rfc3986().decode(null), '',
		'Null value should return empty string');
});
test('The value containing encoded UTF8 characters should be decoded following the RFC3629', function () {
	equal(new Rfc3986().decode('%C3%A5%C3%A7%C3%B1'), 'åçñ',
		'Text names and values MUST be encoded as UTF-8 octets before percent-encoding them');
	equal(new Rfc3986().decode('%E4%BD%A0%E5%A5%BD'), '你好',
		'Text names and values MUST be encoded as UTF-8 octets before percent-encoding them');
});<|endoftext|>


<commit_before>describe( 'Package events', function () {

  var
    Klass,
    pkgDef,
    pkgInst,
    eventNames = [
      'onBegin','onEnd','onEngage','onNode',
      'onRelease','onScope','onTraverse',
      'onTraversed','onTraversing'
    ],
    stuffObj = [],
    stuff = {
      'a':1,
      'a2':'foo',
      'c': stuffObj
    }
  ;

  beforeEach(function () {
    Klass = Panzer.create();
    pkgDef = Klass.pkg('a');
    pkgInst = pkgDef(new Klass(stuff));
  });

  it( 'should scope callbacks to their package-instance', function () {
    eventNames.forEach(function (eventName) {
      pkgDef[eventName] = sinon.spy();
    });
    pkgDef.onTraverse = sinon.spy(function () {
      this.tank.stop();
    });
    pkgInst.tank.go(1);
    pkgInst.tank.go(1);
    eventNames.forEach(function (eventName) {
      pkgDef[eventName].should.have.always.been.calledOn(pkgInst);
    });
  });

  it( 'should occur in the expected order', function () {
    var
      order = [],
      pkgInst = pkgDef(new Klass())
    ;

    function trace(n) {
      return function () {
        order.push(n);
      }
    }

    pkgDef.onBegin = trace(1);
    pkgDef.onNode = trace(2);
    pkgDef.onEngage = trace(3);
    pkgDef.onScope = trace(4);
    pkgDef.onTraverse = trace(5);
    pkgDef.onTraversing = trace(6);
    pkgDef.onTraversed = trace(7);
    pkgDef.onRelease = trace(8);
    pkgDef.onEnd = trace(9);

    pkgInst.tank.go(1);

    // validate simple traversal
    order.should.eql([1,2,3,4,5,7,5,7,8,9]);
    order = [];
    pkgInst.tank.currentIndex.should.equal(1);

    pkgDef.onTraverse = function () {
      this.tank.stop();
      order.push(5);
    };
    pkgInst.tank.go(1);
    // valieate interrupting navigation
    order.should.eql([1,3,5,8,9]);
    order = [];

    pkgDef.onTraverse = trace(5);
    pkgInst.tank.go(1);
    // validate resuming navigation
    order.should.eql([1,3,6,7,5,7,8,9]);

    // test stopping from each event
    [
      ['onBegin', [1,9]],
      ['onEnd', [1,2,3,4,5,7,5,7,8,9]],
      ['onEngage', [1,2,3,8,9]],
      ['onNode', [1,2,9]],
      ['onRelease', [1,2,3,4,5,7,5,7,8,9]],
      ['onScope', [1,2,3,4,8,9]],
      ['onTraverse', [1,2,3,4,5,8,9]],
      ['onTraversed', [1,2,3,4,5,7,8,9]]
    ].forEach(function (seq) {
      var
        eventName = seq[0],
        expectedOrder = seq[1],
        prevCallback = pkgDef[eventName]
      ;
      pkgInst.tank.go(0);
      order = [];

      pkgDef[eventName] = function () {
        this.tank.stop();
        prevCallback();
      };
      pkgInst.tank.go(1);

      order.should.eql(expectedOrder);
      pkgDef[eventName] = prevCallback;
    });
  });

  it( 'should pass the event name as the first callback argument', function () {
    eventNames.forEach(function (eventName) {
      pkgDef[eventName] = sinon.spy();
    });
    pkgDef.onTraverse = sinon.spy(function () {
      this.tank.stop();
    });
    pkgInst.tank.go(1);
    pkgInst.tank.go(1);
    eventNames.forEach(function (eventName) {
      pkgDef[eventName]
        .firstCall.args[0].should.equal(eventName.charAt(2).toLowerCase() +  eventName.substr(3));
    });
   });

  describe( '.onBegin & .onEnd', function () {

    it( 'should trigger when tank.go() is called (outside of a loop)', function () {
      pkgDef.onBegin = pkgDef.onEnd = sinon.spy();
      pkgInst.tank.go();
      pkgDef.onBegin.should.have.been.calledTwice;
    });

  });

  describe( '.onNode', function () {

    it( 'should include indexes for the current and old node', function () {
      pkgDef.onNode = sinon.spy();
      pkgInst.tank.go(1);

      pkgDef.onNode.should.have.been.calledOnce;
      pkgDef.onNode.firstCall.args.should.have.length(3);
      pkgDef.onNode.firstCall.args[1].should.equal(1);
      pkgDef.onNode.firstCall.args[2].should.equal(0);

    });

  });

  describe( '.onRelease and .onEngage', function () {

    it( 'should wrap .onScope and traversal events', function () {
      var EventsBetweenReleaseAndEngage = eventNames.slice(5);
      eventNames.slice(2).forEach(function (eventName) {
        pkgDef[eventName] = sinon.spy();
      });
      pkgDef.onNode = function () {
        if (pkgDef.onEngage.callCount) {
          EventsBetweenReleaseAndEngage.forEach(function (eventName) {
            console.log(eventName);
            if (pkgDef[eventName].called) {
              pkgDef.onEngage.should.have.been.calledBefore(pkgDef[eventName]);
              pkgDef.onRelease.should.have.been.calledAfter(pkgDef[eventName]);
              console.log('OK!');
            } else {
              console.log('NOT CALLED!');
            }
          });
          eventNames.slice(2).forEach(function (eventName) {
            if (eventName !== 'onNode') {
              pkgDef[eventName].reset();
            }
          });
        }
      };
      pkgInst.tank.go(5);
    });

  });

  describe( '.onScope', function () {

    it( 'should indicating entry or exit with a second argument', function () {
      pkgDef.onScope = sinon.spy();
      pkgInst.tank.go(1);
      pkgInst.tank.go(0);

      pkgDef.onScope.should.have.been.calledTwice;
      pkgDef.onScope.firstCall.args[1].should.have.been.ok;
      pkgDef.onScope.secondCall.args[1].should.not.have.been.ok;
    });

  });

  describe( '.onTraversed', function () {

    it( 'should not occur if stopped during traverse/traversing', function () {
      pkgDef.onTraversed = sinon.spy();

      pkgInst.tank.go(0);
      pkgDef.onTraversed.should.have.been.called;
      pkgDef.onTraversed.reset();

      pkgDef.onTraverse = function () {
        this.tank.stop();
      };

      pkgInst.tank.go(0);
      pkgDef.onTraversed.should.not.have.been.called;

    });

  });

  describe( '.onTraversing', function () {

    it( 'should occur if traverse/traversed callback stops navigation', function () {
      pkgDef.onTraverse = pkgDef.onTraversing = function () {
        this.tank.stop();
      };
      pkgDef.onTraversing  = sinon.spy(pkgDef.onTraversing);
      pkgInst.tank.go(0);
      pkgDef.onTraversing.should.not.have.been.called;

      pkgInst.tank.go(0);
      pkgDef.onTraversing.should.have.been.calledOnce;

      pkgInst.tank.go(0);
      pkgDef.onTraversing.should.have.been.calledTwice;
    });

    it( 'should occur on the previous node/phase before resuming navigation', function () {
      var curNode = -1;
      pkgDef.onTraversing = sinon.spy(function () {
        curNode = this.tank.currentIndex;
      });
      pkgDef.onTraverse = sinon.spy(function () {
        this.tank.stop();
      });

      pkgInst.tank.go(3);
      pkgInst.nodes.should.have.length(5);
      pkgDef.onTraversing.should.not.have.been.called;

      pkgInst.tank.go(3);
      pkgDef.onTraversing.should.have.been.calledOnce;
      pkgDef.onTraverse.firstCall.args[1]
        .should.equal(pkgDef.onTraversing.firstCall.args[1]);
      curNode.should.equal(1);
    });

  });

  describe( 'traversals', function () {

    beforeEach(function () {
      pkgDef.onTraverse = pkgDef.onTraversed = sinon.spy();
    });

    it( 'should pass 0 when on a node', function () {
      pkgInst.tank.go(0);
      pkgDef.onTraverse.calledWith(0);
    });

    it( 'should pass 1 when entering a node', function () {
      pkgInst.tank.go(1);
      pkgDef.onTraverse.calledWith(1);
    });

    it( 'should pass 2 when exiting a node', function () {
      pkgInst.tank.go(1);
      pkgInst.tank.go(0);
      pkgDef.onTraverse.calledWith(2);
    });

    it( 'should pass 3 when bypassing a node, down the tree', function () {
      pkgInst.tank.go(3);
      pkgDef.onTraverse.calledWith(3);
    });

    it( 'should pass 4 when bypassing a node, up the tree', function () {
      pkgInst.tank.go(3);
      pkgInst.tank.go(2);
      pkgDef.onTraverse.calledWith(5);
    });

  });

  describe( 'stopping and resuming', function () {

    it( 'should resume when on a node', function () {
      pkgDef.onTraverse = sinon.spy(function (eventName, traversalPhase) {
        if (traversalPhase === 0) {
          this.tank.stop();
        }
      });
      pkgDef.onTraversed = sinon.spy();
      pkgInst.tank.go(0);
      pkgDef.onTraverse.should.have.been.calledOnce;
      pkgDef.onTraverse.reset();
      pkgDef.onTraversed.should.not.have.been.calledOnce;

      pkgDef.onTraversed = sinon.spy(function () {
        this.tank.stop();
      });
      pkgInst.tank.go();
      pkgDef.onTraverse.should.not.have.been.called;
      pkgDef.onTraversed.should.have.been.calledOnce;
      pkgDef.onTraversed = sinon.spy();
      pkgInst.tank.go();
      pkgDef.onTraversed.should.have.been.calledOnce;
    });

    it( 'should resume when entering a node', function () {
      pkgDef.onTraverse = sinon.spy(function (eventName, traversalPhase) {
        if (traversalPhase === 1) {
          this.tank.stop();
        }
      });
      pkgDef.onTraversed = sinon.spy();
      pkgInst.tank.go(1);
      pkgDef.onTraverse.should.have.been.calledOnce;
      pkgDef.onTraverse = sinon.spy();
      pkgDef.onTraversed.should.not.have.been.called;

      pkgDef.onTraversed = sinon.spy(function () {
        this.tank.stop();
      });
      pkgInst.tank.go();
      pkgDef.onTraverse.should.not.have.been.called;
      pkgDef.onTraversed.should.have.been.calledOnce;
      pkgDef.onTraversed = sinon.spy();
      pkgInst.tank.go();
      pkgDef.onTraverse.should.have.been.calledOnce;
      pkgDef.onTraversed.should.have.been.calledTwice;
    });

    it( 'should resume when exiting a node', function () {
      pkgInst.tank.go(1);
      pkgDef.onTraverse = sinon.spy(function (eventName, traversalPhase) {
        if (traversalPhase === 2) {
          this.tank.stop();
        }
      });
      pkgDef.onTraversed = sinon.spy();
      pkgInst.tank.go(0);
      pkgDef.onTraverse.should.have.been.calledOnce;
      pkgDef.onTraverse = sinon.spy();
      pkgDef.onTraversed.should.not.have.been.called;

      pkgDef.onTraversed = sinon.spy(function () {
        this.tank.stop();
      });
      pkgInst.tank.go();
      pkgDef.onTraverse.should.not.have.been.called;
      pkgDef.onTraversed.should.have.been.calledOnce;
      pkgDef.onTraversed = sinon.spy();
      pkgInst.tank.go();
      pkgDef.onTraverse.should.have.been.calledOnce;
      pkgDef.onTraversed.should.have.been.calledTwice;
    });

    it( 'should resume when bypassing a node, down the tree', function () {
      pkgInst.tank.go(1);
      pkgDef.onTraverse = sinon.spy(function (eventName, traversalPhase) {
        if (traversalPhase === 3) {
          this.tank.stop();
        }
      });
      pkgDef.onTraversed = sinon.spy();
      pkgInst.tank.go(3);
      pkgDef.onTraverse.should.have.been.calledOnce;
      pkgDef.onTraverse = sinon.spy();
      pkgDef.onTraversed.should.not.have.been.called;

      pkgDef.onTraversed = sinon.spy(function () {
        this.tank.stop();
      });
      pkgInst.tank.go();
      pkgDef.onTraverse.should.not.have.been.called;
      pkgDef.onTraversed.should.have.been.calledOnce;
      pkgDef.onTraversed = sinon.spy();
      pkgInst.tank.go();
      pkgDef.onTraverse.should.have.been.calledTwice;
      pkgDef.onTraversed.should.have.been.calledThrice;
    });

    it( 'should resume when bypassing a node, up the tree', function () {
      pkgInst.tank.go(3);
      pkgDef.onTraverse = sinon.spy(function (eventName, traversalPhase) {
        if (traversalPhase === 4) {
          this.tank.stop();
        }
      });
      pkgDef.onTraversed = sinon.spy();
      pkgInst.tank.go(1);
      pkgDef.onTraverse.should.have.been.calledTwice;
      pkgDef.onTraverse = sinon.spy();
      pkgDef.onTraversed.should.have.been.calledOnce;

      pkgDef.onTraversed = sinon.spy(function () {
        this.tank.stop();
      });
      pkgInst.tank.go();
      pkgDef.onTraverse.should.not.have.been.called;
      pkgDef.onTraversed.should.have.been.calledOnce;
      pkgDef.onTraversed = sinon.spy();
      pkgInst.tank.go();
      pkgDef.onTraverse.should.have.been.calledOnce;
      pkgDef.onTraversed.should.have.been.calledTwice;
    });

  });

  describe( 'navigation', function () {

    it( 'should allow stopping towards parent and redirecting to older sibling', function () {
      var phases = [];

      pkgInst.tank.go(2);
      pkgDef.onTraverse = function (evt, phase) {
        expect(phase).to.equal(2);
        this.tank.currentIndex.should.equal(2);
        this.tank.stop();
      };
      pkgInst.tank.currentIndex.should.equal(2);
      pkgInst.tank.go(0);
      pkgInst.tank.currentIndex.should.equal(2);

      pkgDef.onTraversing = sinon.spy(function (evt, phase) {
        this.tank.currentIndex.should.equal(2);
      });
      pkgDef.onTraverse = function (evt, phase) {
        phases.push([this.tank.currentIndex, phase]);
      };

      pkgInst.tank.go(3);
      pkgDef.onTraversing.should.have.been.calledOnce;
      pkgInst.tank.currentIndex.should.equal(3);
      phases.should.eql([[3,1], [3,0]]);
    });

  });

});<commit_msg>more tests for good measure<commit_after>describe( 'Package events', function () {

  var
    Klass,
    pkgDef,
    pkgInst,
    eventNames = [
      'onBegin','onEnd','onEngage','onNode',
      'onRelease','onScope','onTraverse',
      'onTraversed','onTraversing'
    ],
    stuffObj = [],
    stuff = {
      'a':1,
      'a2':'foo',
      'c': stuffObj
    }
  ;

  beforeEach(function () {
    Klass = Panzer.create();
    pkgDef = Klass.pkg('a');
    pkgInst = pkgDef(new Klass(stuff));
  });

  it( 'should scope callbacks to their package-instance', function () {
    eventNames.forEach(function (eventName) {
      pkgDef[eventName] = sinon.spy();
    });
    pkgDef.onTraverse = sinon.spy(function () {
      this.tank.stop();
    });
    pkgInst.tank.go(1);
    pkgInst.tank.go(1);
    eventNames.forEach(function (eventName) {
      pkgDef[eventName].should.have.always.been.calledOn(pkgInst);
    });
  });

  it( 'should occur in the expected order', function () {
    var
      order = [],
      pkgInst = pkgDef(new Klass())
    ;

    function trace(n) {
      return function () {
        order.push(n);
      }
    }

    pkgDef.onBegin = trace(1);
    pkgDef.onNode = trace(2);
    pkgDef.onEngage = trace(3);
    pkgDef.onScope = trace(4);
    pkgDef.onTraverse = trace(5);
    pkgDef.onTraversing = trace(6);
    pkgDef.onTraversed = trace(7);
    pkgDef.onRelease = trace(8);
    pkgDef.onEnd = trace(9);

    pkgInst.tank.go(1);

    // validate simple traversal
    order.should.eql([1,2,3,4,5,7,5,7,8,9]);
    order = [];
    pkgInst.tank.currentIndex.should.equal(1);

    pkgDef.onTraverse = function () {
      this.tank.stop();
      order.push(5);
    };
    pkgInst.tank.go(1);
    // valieate interrupting navigation
    order.should.eql([1,3,5,8,9]);
    order = [];

    pkgDef.onTraverse = trace(5);
    pkgInst.tank.go(1);
    // validate resuming navigation
    order.should.eql([1,3,6,7,5,7,8,9]);

    // test stopping from each event
    [
      ['onBegin', [1,9]],
      ['onEnd', [1,2,3,4,5,7,5,7,8,9]],
      ['onEngage', [1,2,3,8,9]],
      ['onNode', [1,2,9]],
      ['onRelease', [1,2,3,4,5,7,5,7,8,9]],
      ['onScope', [1,2,3,4,8,9]],
      ['onTraverse', [1,2,3,4,5,8,9]],
      ['onTraversed', [1,2,3,4,5,7,8,9]]
    ].forEach(function (seq) {
      var
        eventName = seq[0],
        expectedOrder = seq[1],
        prevCallback = pkgDef[eventName]
      ;
      pkgInst.tank.go(0);
      order = [];

      pkgDef[eventName] = function () {
        this.tank.stop();
        prevCallback();
      };
      pkgInst.tank.go(1);

      order.should.eql(expectedOrder);
      pkgDef[eventName] = prevCallback;
    });
  });

  it( 'should pass the event name as the first callback argument', function () {
    eventNames.forEach(function (eventName) {
      pkgDef[eventName] = sinon.spy();
    });
    pkgDef.onTraverse = sinon.spy(function () {
      this.tank.stop();
    });
    pkgInst.tank.go(1);
    pkgInst.tank.go(1);
    eventNames.forEach(function (eventName) {
      pkgDef[eventName]
        .firstCall.args[0].should.equal(eventName.charAt(2).toLowerCase() +  eventName.substr(3));
    });
   });

  describe( '.onBegin & .onEnd', function () {

    it( 'should trigger when tank.go() is called (outside of a loop)', function () {
      pkgDef.onBegin = pkgDef.onEnd = sinon.spy();
      pkgInst.tank.go();
      pkgDef.onBegin.should.have.been.calledTwice;
    });

  });

  describe( '.onNode', function () {

    it( 'should include indexes for the current and old node', function () {
      pkgDef.onNode = sinon.spy();
      pkgInst.tank.go(1);

      pkgDef.onNode.should.have.been.calledOnce;
      pkgDef.onNode.firstCall.args.should.have.length(3);
      pkgDef.onNode.firstCall.args[1].should.equal(1);
      pkgDef.onNode.firstCall.args[2].should.equal(0);

    });

  });

  describe( '.onRelease and .onEngage', function () {

    it( 'should wrap .onScope and traversal events', function () {
      var EventsBetweenReleaseAndEngage = eventNames.slice(5);
      eventNames.slice(2).forEach(function (eventName) {
        pkgDef[eventName] = sinon.spy();
      });
      pkgDef.onNode = function () {
        if (pkgDef.onEngage.callCount) {
          EventsBetweenReleaseAndEngage.forEach(function (eventName) {
            console.log(eventName);
            if (pkgDef[eventName].called) {
              pkgDef.onEngage.should.have.been.calledBefore(pkgDef[eventName]);
              pkgDef.onRelease.should.have.been.calledAfter(pkgDef[eventName]);
              console.log('OK!');
            } else {
              console.log('NOT CALLED!');
            }
          });
          eventNames.slice(2).forEach(function (eventName) {
            if (eventName !== 'onNode') {
              pkgDef[eventName].reset();
            }
          });
        }
      };
      pkgInst.tank.go(5);
    });

  });

  describe( '.onScope', function () {

    it( 'should indicating entry or exit with a second argument', function () {
      pkgDef.onScope = sinon.spy();
      pkgInst.tank.go(1);
      pkgInst.tank.go(0);

      pkgDef.onScope.should.have.been.calledTwice;
      pkgDef.onScope.firstCall.args[1].should.have.been.ok;
      pkgDef.onScope.secondCall.args[1].should.not.have.been.ok;
    });

  });

  describe( '.onTraversed', function () {

    it( 'should not occur if stopped during traverse/traversing', function () {
      pkgDef.onTraversed = sinon.spy();

      pkgInst.tank.go(0);
      pkgDef.onTraversed.should.have.been.called;
      pkgDef.onTraversed.reset();

      pkgDef.onTraverse = function () {
        this.tank.stop();
      };

      pkgInst.tank.go(0);
      pkgDef.onTraversed.should.not.have.been.called;

    });

  });

  describe( '.onTraversing', function () {

    it( 'should expect two arguments', function () {
      pkgDef.onTraverse = function () {
        this.tank.stop();
      };
      pkgDef.onTraversing = sinon.spy();
      pkgInst.tank.go(1);
      pkgInst.tank.go(0);
      pkgDef.onTraversing.should.have.been.calledOnce;
      pkgDef.onTraversing.firstCall.args.should.have.length(2);
    });

    it( 'should be passed a number as the second argument', function () {
      pkgDef.onTraverse = function () {
        this.tank.stop();
      };
      pkgDef.onTraversing = sinon.spy();
      pkgInst.tank.go(1);
      pkgInst.tank.go(0);
      pkgDef.onTraversing.should.have.been.calledOnce;
      expect(pkgDef.onTraversing.firstCall.args[1]).to.be.a.number;
    });

    it( 'should occur if traverse/traversed callback stops navigation', function () {
      pkgDef.onTraverse = pkgDef.onTraversing = function () {
        this.tank.stop();
      };
      pkgDef.onTraversing  = sinon.spy(pkgDef.onTraversing);
      pkgInst.tank.go(0);
      pkgDef.onTraversing.should.not.have.been.called;

      pkgInst.tank.go(0);
      pkgDef.onTraversing.should.have.been.calledOnce;

      pkgInst.tank.go(0);
      pkgDef.onTraversing.should.have.been.calledTwice;
    });

    it( 'should occur on the previous node/phase before resuming navigation', function () {
      var curNode = -1;
      pkgDef.onTraversing = sinon.spy(function () {
        curNode = this.tank.currentIndex;
      });
      pkgDef.onTraverse = sinon.spy(function () {
        this.tank.stop();
      });

      pkgInst.tank.go(3);
      pkgInst.nodes.should.have.length(5);
      pkgDef.onTraversing.should.not.have.been.called;

      pkgInst.tank.go(3);
      pkgDef.onTraversing.should.have.been.calledOnce;
      pkgDef.onTraverse.firstCall.args[1]
        .should.equal(pkgDef.onTraversing.firstCall.args[1]);
      curNode.should.equal(1);
    });

    it( 'should occur with the same node and phase as the previous .onTraverse', function () {
      var
        curNode = -1,
        phases = []
      ;
      pkgDef.onTraversing = function (evt, phase) {
        phases.push([this.tank.currentIndex, phase]);
      };
      pkgDef.onTraverse = function (evt, phase) {
        this.tank.stop();
        phases.push([this.tank.currentIndex, phase]);
      };

      pkgInst.tank.go(1);
      pkgInst.tank.go(0);

      phases.should.have.length.above(1);
      expect(phases[0][1]).to.equal(phases[1][1]);
    });

  });

  describe( 'traversals', function () {

    beforeEach(function () {
      pkgDef.onTraverse = pkgDef.onTraversed = sinon.spy();
    });

    it( 'should pass 0 when on a node', function () {
      pkgInst.tank.go(0);
      pkgDef.onTraverse.calledWith(0);
    });

    it( 'should pass 1 when entering a node', function () {
      pkgInst.tank.go(1);
      pkgDef.onTraverse.calledWith(1);
    });

    it( 'should pass 2 when exiting a node', function () {
      pkgInst.tank.go(1);
      pkgInst.tank.go(0);
      pkgDef.onTraverse.calledWith(2);
    });

    it( 'should pass 3 when bypassing a node, down the tree', function () {
      pkgInst.tank.go(3);
      pkgDef.onTraverse.calledWith(3);
    });

    it( 'should pass 4 when bypassing a node, up the tree', function () {
      pkgInst.tank.go(3);
      pkgInst.tank.go(2);
      pkgDef.onTraverse.calledWith(5);
    });

  });

  describe( 'stopping and resuming', function () {

    it( 'should resume when on a node', function () {
      pkgDef.onTraverse = sinon.spy(function (eventName, traversalPhase) {
        if (traversalPhase === 0) {
          this.tank.stop();
        }
      });
      pkgDef.onTraversed = sinon.spy();
      pkgInst.tank.go(0);
      pkgDef.onTraverse.should.have.been.calledOnce;
      pkgDef.onTraverse.reset();
      pkgDef.onTraversed.should.not.have.been.calledOnce;

      pkgDef.onTraversed = sinon.spy(function () {
        this.tank.stop();
      });
      pkgInst.tank.go();
      pkgDef.onTraverse.should.not.have.been.called;
      pkgDef.onTraversed.should.have.been.calledOnce;
      pkgDef.onTraversed = sinon.spy();
      pkgInst.tank.go();
      pkgDef.onTraversed.should.have.been.calledOnce;
    });

    it( 'should resume when entering a node', function () {
      pkgDef.onTraverse = sinon.spy(function (eventName, traversalPhase) {
        if (traversalPhase === 1) {
          this.tank.stop();
        }
      });
      pkgDef.onTraversed = sinon.spy();
      pkgInst.tank.go(1);
      pkgDef.onTraverse.should.have.been.calledOnce;
      pkgDef.onTraverse = sinon.spy();
      pkgDef.onTraversed.should.not.have.been.called;

      pkgDef.onTraversed = sinon.spy(function () {
        this.tank.stop();
      });
      pkgInst.tank.go();
      pkgDef.onTraverse.should.not.have.been.called;
      pkgDef.onTraversed.should.have.been.calledOnce;
      pkgDef.onTraversed = sinon.spy();
      pkgInst.tank.go();
      pkgDef.onTraverse.should.have.been.calledOnce;
      pkgDef.onTraversed.should.have.been.calledTwice;
    });

    it( 'should resume when exiting a node', function () {
      pkgInst.tank.go(1);
      pkgDef.onTraverse = sinon.spy(function (eventName, traversalPhase) {
        if (traversalPhase === 2) {
          this.tank.stop();
        }
      });
      pkgDef.onTraversed = sinon.spy();
      pkgInst.tank.go(0);
      pkgDef.onTraverse.should.have.been.calledOnce;
      pkgDef.onTraverse = sinon.spy();
      pkgDef.onTraversed.should.not.have.been.called;

      pkgDef.onTraversed = sinon.spy(function () {
        this.tank.stop();
      });
      pkgInst.tank.go();
      pkgDef.onTraverse.should.not.have.been.called;
      pkgDef.onTraversed.should.have.been.calledOnce;
      pkgDef.onTraversed = sinon.spy();
      pkgInst.tank.go();
      pkgDef.onTraverse.should.have.been.calledOnce;
      pkgDef.onTraversed.should.have.been.calledTwice;
    });

    it( 'should resume when bypassing a node, down the tree', function () {
      pkgInst.tank.go(1);
      pkgDef.onTraverse = sinon.spy(function (eventName, traversalPhase) {
        if (traversalPhase === 3) {
          this.tank.stop();
        }
      });
      pkgDef.onTraversed = sinon.spy();
      pkgInst.tank.go(3);
      pkgDef.onTraverse.should.have.been.calledOnce;
      pkgDef.onTraverse = sinon.spy();
      pkgDef.onTraversed.should.not.have.been.called;

      pkgDef.onTraversed = sinon.spy(function () {
        this.tank.stop();
      });
      pkgInst.tank.go();
      pkgDef.onTraverse.should.not.have.been.called;
      pkgDef.onTraversed.should.have.been.calledOnce;
      pkgDef.onTraversed = sinon.spy();
      pkgInst.tank.go();
      pkgDef.onTraverse.should.have.been.calledTwice;
      pkgDef.onTraversed.should.have.been.calledThrice;
    });

    it( 'should resume when bypassing a node, up the tree', function () {
      pkgInst.tank.go(3);
      pkgDef.onTraverse = sinon.spy(function (eventName, traversalPhase) {
        if (traversalPhase === 4) {
          this.tank.stop();
        }
      });
      pkgDef.onTraversed = sinon.spy();
      pkgInst.tank.go(1);
      pkgDef.onTraverse.should.have.been.calledTwice;
      pkgDef.onTraverse = sinon.spy();
      pkgDef.onTraversed.should.have.been.calledOnce;

      pkgDef.onTraversed = sinon.spy(function () {
        this.tank.stop();
      });
      pkgInst.tank.go();
      pkgDef.onTraverse.should.not.have.been.called;
      pkgDef.onTraversed.should.have.been.calledOnce;
      pkgDef.onTraversed = sinon.spy();
      pkgInst.tank.go();
      pkgDef.onTraverse.should.have.been.calledOnce;
      pkgDef.onTraversed.should.have.been.calledTwice;
    });

  });

  describe( 'navigation', function () {

    it( 'should allow stopping towards parent and redirecting to older sibling', function () {
      var phases = [];

      pkgInst.tank.go(2);
      pkgDef.onTraverse = function (evt, phase) {
        expect(phase).to.equal(2);
        this.tank.currentIndex.should.equal(2);
        this.tank.stop();
      };
      pkgInst.tank.currentIndex.should.equal(2);
      pkgInst.tank.go(0);
      pkgInst.tank.currentIndex.should.equal(2);

      pkgDef.onTraversing = sinon.spy(function (evt, phase) {
        this.tank.currentIndex.should.equal(2);
      });
      pkgDef.onTraverse = function (evt, phase) {
        phases.push([this.tank.currentIndex, phase]);
      };

      pkgInst.tank.go(3);
      pkgDef.onTraversing.should.have.been.calledOnce;
      pkgInst.tank.currentIndex.should.equal(3);
      phases.should.eql([[3,1], [3,0]]);
    });

  });

});<|endoftext|>


<commit_before>describe('ax5.util.number TEST', function () {
    var testCases = [
        {
            args: [
                123456789.678,
                {
                    round: 1
                }
            ],
            expect: 123456789.7,
            explanation: 123456789.678 + ', { round: 1 }'
        },
        {
            args: [
                123456789.678,
                {
                    round: 1,
                    money: true
                }
            ],
            expect: '123,456,789.7',
            explanation: 123456789.678 + ', { round: 1, money: true }'
        },
        {
            args: [
                123456789.678,
                {
                    round: 2,
                    byte: true
                }
            ],
            expect: '117.7MB',
            explanation: 123456789.678 + ', { round: 2, byte: true }'
        },
        {
            args: [
                -123456789.678,
                {
                    abs: true,
                    round: 2,
                    money: true
                }
            ],
            expect: '123,456,789.68',
            explanation: -123456789.678 + ',{ abs: true, round: 2, money: true }'
        },
        {
            args: [
                -123456789.678,
                {
                    abs: true,
                    ceil: true,
                    money: true
                }
            ],
            expect: '123,456,790',
            explanation: -123456789.678 + ',{ abs: true, ceil: true, money: true }'
        },
        {
            args: [
                -123456789.678,
                {
                    abs: true,
                    floor: true,
                    money: true
                }
            ],
            expect: '123,456,789',
            explanation: -123456789.678 + ',{ abs: true, floor: true, money: true }'
        },
        {
            args: [
                1024 * 1024,
                {
                    byte: true,
                }
            ],
            expect: '1024KB',
            explanation: 1024 * 1024 + ',{ byte: true}'
        },
        {
            args: [
                1024 * 1024 * 5,
                {
                    byte: true,
                }
            ],
            expect: '5MB',
            explanation: 1024 * 1024 * 5 + ',{ byte: true}'
        },
        {
            args: [
                1024 * 1024 * 1024,
                {
                    byte: true,
                }
            ],
            expect: '1024MB',
            explanation: 1024 * 1024 * 1024 + ',{ byte: true}'
        },
        {
            args: [
                'A-1234~~56789.8~888PX',
                {
                    abs: true,
                    round: 2,
                    money: true
                }
            ],
            expect: '123,456,789.89',
            explanation: 'A-1234~~56789.8~888PX , { abs: true, round: 2, money: true }'
        }
    ];

    testCases.forEach(function (testCase) {
        it('ax5.util.number(' + testCase.explanation + ') expect ' + testCase.expect, function () {
            var actual = ax5.util.number.apply(this, testCase.args);

            actual.should.deepEqual(testCase.expect);
        });
    });
});
<commit_msg>update test code<commit_after>describe('ax5.util.number TEST', function () {
    var testCases = [
        {
            args: [
                123456789.678,
                {
                    round: 1
                }
            ],
            expect: 123456789.7,
            explanation: 123456789.678 + ', { round: 1 }'
        },
        {
            args: [
                123456789.678,
                {
                    round: 1,
                    money: true
                }
            ],
            expect: '123,456,789.7',
            explanation: 123456789.678 + ', { round: 1, money: true }'
        },
        {
            args: [
                123456789.678,
                {
                    round: 2,
                    byte: true
                }
            ],
            expect: '117.7MB',
            explanation: 123456789.678 + ', { round: 2, byte: true }'
        },
        {
            args: [
                -123456789.678,
                {
                    abs: true,
                    round: 2,
                    money: true
                }
            ],
            expect: '123,456,789.68',
            explanation: -123456789.678 + ',{ abs: true, round: 2, money: true }'
        },
        {
            args: [
                -123456789.678,
                {
                    abs: true,
                    ceil: true,
                    money: true
                }
            ],
            expect: '123,456,790',
            explanation: -123456789.678 + ',{ abs: true, ceil: true, money: true }'
        },
        {
            args: [
                -123456789.678,
                {
                    abs: true,
                    floor: true,
                    money: true
                }
            ],
            expect: '123,456,789',
            explanation: -123456789.678 + ',{ abs: true, floor: true, money: true }'
        },
        {
            args: [
                1024 * 1024,
                {
                    byte: true,
                }
            ],
            expect: '1024KB',
            explanation: 1024 * 1024 + ',{byte: true}'
        },
        {
            args: [
                1024 * 1024 * 5,
                {
                    byte: true,
                }
            ],
            expect: '5MB',
            explanation: 1024 * 1024 * 5 + ',{byte: true}'
        },
        {
            args: [
                1024 * 1024 * 1024,
                {
                    byte: true,
                }
            ],
            expect: '1024MB',
            explanation: 1024 * 1024 * 1024 + ',{byte: true}'
        },
        {
            args: [
                1024 * 1024 * 1024 * 5,
                {
                    byte: true,
                }
            ],
            expect: '5GB',
            explanation: 1024 * 1024 * 1024 + ',{byte: true}'
        },
        {
            args: [
                'A-1234~~56789.8~888PX',
                {
                    abs: true,
                    round: 2,
                    money: true
                }
            ],
            expect: '123,456,789.89',
            explanation: 'A-1234~~56789.8~888PX , { abs: true, round: 2, money: true }'
        }
    ];

    testCases.forEach(function (testCase) {
        it('ax5.util.number(' + testCase.explanation + ') expect ' + testCase.expect, function () {
            var actual = ax5.util.number.apply(this, testCase.args);

            actual.should.deepEqual(testCase.expect);
        });
    });
});
<|endoftext|>