<commit_before>configure({
    name: 'Sample Test Suite',
    hidePassedGroups: true
});

group('This group will intentionally fail', function(){
    test('true === false', function(){
        isTrue(false, 'Yes, true === false!');
    });
});

group('Does it work?', function(){
    test('Hello World!', function(){
        var hw = 'Hello World!';
        isTrue(hw === 'Hello World!', 'Yes, it works!');
    });
});

group('Truthy boolean evaluation', function(){
    var undef;
    var def = {};
    var one = 1;
    var fls = false;
    var tru = true;
    test('When using truthy boolean evaluation', function(){
        isNotTruthy(undef, 'undefined is false');
        isTruthy(def, 'a valid reference is true');
        isTruthy(one, '1 is true');
        isNotTruthy(fls, 'false is false');
        isTruthy(tru, 'true is true');
    });
});

group('Assertions', function(){
    test('Does a equal b', function(){
        var char = 'b';
        var a = {a: 'a', b: 'b'};
        var b = {a: 'a', b: b};
        var c = {a: 'a', b: 'b'};
        var three = 3;
        notEqual(a, b, 'No way these are equal.');
        equal(a, c, 'But these are equal.');
        equal(char, 'b', 'Yup, these are equal too.');
        equal(3, three, 'And so are these.');
    });
});

group('2 synchronous test with "beforeEachTest"', function(){
    var count = 0;
    beforeEachTest(function(){
        count = 1;
    });
    test('Is count 1?', function(){
        isFalse(count === 0, 'count doesn\'t equal 0');
        isTrue(count === 1, 'count does equal 1');
        isTrue((count += 1) === 2, 'count now equals 2');
    });
    test('Is count still 2?', function(){
        isFalse(count === 2, 'nope, it isn\'t still 2');
        isTrue(count === 1, 'now count equals 1');
    });
});

group('2 synchronous test with "afterEachTest"', function(){
    var count = 0;
    afterEachTest(function(){
        count = 1;
    });
    test('Is count 0?', function(){
        isTrue(count === 0, 'count does equal 0.');
    });
    test('Is count still 0?', function(){
        isFalse(count === 0, 'count doesn\'t equal 0.');
        isTrue(count === 1, 'count now equals 1.');
    });
});

group('An asynchronous test with no before/after eachTest', function(){
    asyncTest('Isn\'t JavaScript amazing?', function(){
        var val;
        setTimeout(function(){
            val = 'Isn\'t JavaScript amazing?';
        }, 10);
        whenAsyncDone(function(){
            isTrue(val === 'Isn\'t JavaScript amazing?', 'Yest it is!');
        });
    });
});

group('2 asynchronous tests with "asyncBeforeEachTest"', function(){
    var count = 0;
    asyncBeforeEachTest(function(){
        count = 2;
    });
    asyncTest('Is count 20?', function(){
        setTimeout(function(){
            count *= 10;
        }, 1);

        whenAsyncDone(function(){
            isTrue(count === 20, 'Yes, count now is 20');
        });
    });
    asyncTest('Is count 200?', function(){
        setTimeout(function(){
            count *= 100;
        }, 1);

        whenAsyncDone(function(){
            isTrue(count === 200, 'Yes, count now is 200');
        });
    });
});

group('2 asynchronous tests with "asyncAfterEachTest"', function(){
    var count = 0;
    asyncAfterEachTest(function(){
        count = 1;
    });
    asyncTest('Is count 10?', function(){
        setTimeout(function(){
            count = 10;
        }, 1);

        whenAsyncDone(function(){
            isTrue(count === 10, 'Yes, count now is 10');
        });
    });
    asyncTest('Is count 100?', function(){
        setTimeout(function(){
            count *= 100;
        }, 1);

        whenAsyncDone(function(){
            isTrue(count === 100, 'Yes, count now is 100');
        });
    });
});

group('The asyncBeforeEachTest can pass a value to asyncTest', function(){
    asyncBeforeEachTest(function(){
        return {num: 1};
    });
    asyncTest('Was object passed?', function(val){
        setTimeout(function(){
            val.num *= 100;
        }, 10);

        whenAsyncDone(function(){
            isFalse(typeof val === 'undefined', '{num: 1} was passed to asyncTest');
            isTrue(val.num === 100, '{num: 1} was changed to {num: 100}');
        });
    });
});

group('proxy captures calling information which can be tested against', function(){
    asyncTest('proxy a function calling it twice', function(){
        var fn = proxy(function(s){
            return s;
        });
        setTimeout(function(){
            fn('Somewhere over');
            fn('the rainbow');
        }, 10);
        whenAsyncDone(function(){
            isTrue(fn.wasCalled(2), 'fn was called twice.');
            equal(fn.getCalledCount(), 2, 'fn was called twice.');
            var info1 = fn.getData(0);
            isTrue(info1.argsPassed[0] === 'Somewhere over', 'fn was passed "Somewhere over"');
            isTrue(info1.returned === 'Somewhere over', 'fn returned "Somewhere over"');
            isTrue(info1.context === undefined, 'the 1st time fn was called with global context');
            var info2 = fn.getData(1);
            isTrue(info2.argsPassed[0] === 'the rainbow', 'fn was passed "the rainbow"');
            isTrue(info2.returned === 'the rainbow', 'fn returned "Somewhere over"');
            isTrue(info2.context === undefined, 'the 2nd time fn was called with global context');
        });
    });
    asyncTest('proxy two functions calling each once', function(){
        var fn1 = proxy(function(s){
            return s;
        });
        var fn2 = proxy(function(s){
            return s;
        });
        setTimeout(function(){
            fn1('Somewhere over ');
            fn2('the rainbow');
        }, 10);
        whenAsyncDone(function(){
            isTrue(fn1.wasCalled(1), 'fn1 was called once.');
            equal(fn1.getCalledCount(), 1, 'fn1 was called once.');
            isTrue(fn2.wasCalled(1), 'fn2 was called once.');
            equal(fn2.getCalledCount(), 1, 'fn2 was called once.');
            var fn1Info = fn1.getData(0);
            isTrue(fn1Info.argsPassed[0] === 'Somewhere over ', 'fn1 was passed "Somewhere over "');
            isTrue(fn1Info.returned === 'Somewhere over ', 'fn1 returned "Somewhere over "');
            isTrue(fn1Info.context === undefined, 'fn1 was called with the global context');
            var fn2Info = fn2.getData(0);
            isTrue(fn2Info.argsPassed[0] === 'the rainbow', 'fn2 was passed "the rainbow"');
            isTrue(fn2Info.returned === 'the rainbow', 'fn2 returned "the rainbow"');
            isTrue(fn2Info.context === undefined, 'fn2 was called with the global context');
        });
    });
    asyncTest('proxy a property method calling it twice', function(){
        var foo = {
            title: '',
            name: proxy(function(s){this.title += s; return s;})
        };
        proxy(foo, 'name');
        setTimeout(function(){
            foo.name('Somewhere over ');
            foo.name('the rainbow');
        }, 10);
        whenAsyncDone(function(){
            equal(foo.title, 'Somewhere over the rainbow', 'foo.title = "Somewhere over the rainbow"');
            isTrue(foo.name.wasCalled(2), 'callback was called twice.');
            equal(foo.name.getCalledCount(), 2, 'callback was called twice.');
            var info1 = foo.name.getData(0);
            isTrue(info1.argsPassed[0] === 'Somewhere over ', 'callback was passed "Somewhere over"');
            isTrue(info1.returned === 'Somewhere over ', 'callback returned "Somewhere over "');
            isTrue(info1.context === foo, 'foo.name was called the 1st time with the foo context');
            var info2 = foo.name.getData(1);
            isTrue(info2.argsPassed[0] === 'the rainbow', 'callback was passed "the rainbow"');
            isTrue(info2.returned === 'the rainbow', 'callback returned "the rainbow"');
            isTrue(info2.context === foo, 'foo.name was called the 2nd time with the foo context');
        });
    });
});
<commit_msg>Making more changes to sample-test.js.<commit_after>configure({
    name: 'Sample Test Suite',
    hidePassedGroups: true
});

group('This group will intentionally fail', function(){
    test('true === false', function(){
        isTrue(false, 'Yes, true === false!');
    });
});

group('Does it work?', function(){
    test('Hello World!', function(){
        var hw = 'Hello World!';
        isTrue(hw === 'Hello World!', 'Yes, it works!');
    });
});

group('Truthy boolean evaluation', function(){
    var undef;
    var def = {};
    var one = 1;
    var fls = false;
    var tru = true;
    test('When using truthy boolean evaluation', function(){
        isNotTruthy(undef, 'undefined is false');
        isTruthy(def, 'a valid reference is true');
        isTruthy(one, '1 is true');
        isNotTruthy(fls, 'false is false');
        isTruthy(tru, 'true is true');
    });
});

group('Assertions', function(){
    test('Does a equal b', function(){
        var char = 'b';
        var a = {a: 'a', b: 'b'};
        var b = {a: 'a', b: b};
        var c = {a: 'a', b: 'b'};
        var three = 3;
        notEqual(a, b, 'No way these are equal.');
        equal(a, c, 'But these are equal.');
        equal(char, 'b', 'Yup, these are equal too.');
        equal(3, three, 'And so are these.');
    });
});

group('2 synchronous test with "beforeEachTest"', function(){
    var count = 0;
    beforeEachTest(function(){
        count = 1;
    });
    test('Is count 1?', function(){
        isFalse(count === 0, 'count doesn\'t equal 0');
        isTrue(count === 1, 'count does equal 1');
        isTrue((count += 1) === 2, 'count now equals 2');
    });
    test('Is count still 2?', function(){
        isFalse(count === 2, 'nope, it isn\'t still 2');
        isTrue(count === 1, 'now count equals 1');
    });
});

group('2 synchronous test with "afterEachTest"', function(){
    var count = 0;
    afterEachTest(function(){
        count = 1;
    });
    test('Is count 0?', function(){
        isTrue(count === 0, 'count does equal 0.');
    });
    test('Is count still 0?', function(){
        isFalse(count === 0, 'count doesn\'t equal 0.');
        isTrue(count === 1, 'count now equals 1.');
    });
});

group('An asynchronous test with no before/after eachTest', function(){
    asyncTest('Isn\'t JavaScript amazing?', function(){
        var val;
        setTimeout(function(){
            val = 'Isn\'t JavaScript amazing?';
        }, 1);
        whenAsyncDone(function(){
            isTrue(val === 'Isn\'t JavaScript amazing?', 'Yest it is!');
        });
    });
});

group('2 asynchronous tests with "asyncBeforeEachTest"', function(){
    var count = 0;
    asyncBeforeEachTest(function(){
        count = 2;
    });
    asyncTest('Is count 20?', function(){
        setTimeout(function(){
            count *= 10;
        }, 1);

        whenAsyncDone(function(){
            isTrue(count === 20, 'Yes, count now is 20');
        });
    });
    asyncTest('Is count 200?', function(){
        setTimeout(function(){
            count *= 100;
        }, 1);

        whenAsyncDone(function(){
            isTrue(count === 200, 'Yes, count now is 200');
        });
    });
});

group('2 asynchronous tests with "asyncAfterEachTest"', function(){
    var count = 0;
    asyncAfterEachTest(function(){
        count = 1;
    });
    asyncTest('Is count 10?', function(){
        setTimeout(function(){
            count = 10;
        }, 1);

        whenAsyncDone(function(){
            isTrue(count === 10, 'Yes, count now is 10');
        });
    });
    asyncTest('Is count 100?', function(){
        setTimeout(function(){
            count *= 100;
        }, 1);

        whenAsyncDone(function(){
            isTrue(count === 100, 'Yes, count now is 100');
        });
    });
});

group('The asyncBeforeEachTest can pass a value to asyncTest', function(){
    asyncBeforeEachTest(function(){
        return {num: 1};
    });
    asyncTest('Was object passed?', function(val){
        setTimeout(function(){
            val.num *= 100;
        }, 1);

        whenAsyncDone(function(){
            isFalse(typeof val === 'undefined', '{num: 1} was passed to asyncTest');
            isTrue(val.num === 100, '{num: 1} was changed to {num: 100}');
        });
    });
});

group('proxy captures calling information which can be tested against', function(){
    asyncTest('proxy a function calling it twice', function(){
        var fn = proxy(function(s){
            return s;
        });
        setTimeout(function(){
            fn('Somewhere over');
            fn('the rainbow');
        }, 1);
        whenAsyncDone(function(){
            isTrue(fn.wasCalled(2), 'fn was called twice.');
            equal(fn.getCalledCount(), 2, 'fn was called twice.');
            var info1 = fn.getData(0);
            isTrue(info1.argsPassed[0] === 'Somewhere over', 'fn was passed "Somewhere over"');
            isTrue(info1.returned === 'Somewhere over', 'fn returned "Somewhere over"');
            isTrue(info1.context === undefined, 'the 1st time fn was called with global context');
            var info2 = fn.getData(1);
            isTrue(info2.argsPassed[0] === 'the rainbow', 'fn was passed "the rainbow"');
            isTrue(info2.returned === 'the rainbow', 'fn returned "Somewhere over"');
            isTrue(info2.context === undefined, 'the 2nd time fn was called with global context');
        });
    });
    asyncTest('proxy two functions calling each once', function(){
        var fn1 = proxy(function(s){
            return s;
        });
        var fn2 = proxy(function(s){
            return s;
        });
        setTimeout(function(){
            fn1('Somewhere over ');
            fn2('the rainbow');
        }, 1);
        whenAsyncDone(function(){
            isTrue(fn1.wasCalled(1), 'fn1 was called once.');
            equal(fn1.getCalledCount(), 1, 'fn1 was called once.');
            isTrue(fn2.wasCalled(1), 'fn2 was called once.');
            equal(fn2.getCalledCount(), 1, 'fn2 was called once.');
            var fn1Info = fn1.getData(0);
            isTrue(fn1Info.argsPassed[0] === 'Somewhere over ', 'fn1 was passed "Somewhere over "');
            isTrue(fn1Info.returned === 'Somewhere over ', 'fn1 returned "Somewhere over "');
            isTrue(fn1Info.context === undefined, 'fn1 was called with the global context');
            var fn2Info = fn2.getData(0);
            isTrue(fn2Info.argsPassed[0] === 'the rainbow', 'fn2 was passed "the rainbow"');
            isTrue(fn2Info.returned === 'the rainbow', 'fn2 returned "the rainbow"');
            isTrue(fn2Info.context === undefined, 'fn2 was called with the global context');
        });
    });
    asyncTest('proxy a property method calling it twice', function(){
        var foo = {
            title: '',
            name: proxy(function(s){this.title += s; return s;})
        };
        proxy(foo, 'name');
        setTimeout(function(){
            foo.name('Somewhere over ');
            foo.name('the rainbow');
        }, 1);
        whenAsyncDone(function(){
            equal(foo.title, 'Somewhere over the rainbow', 'foo.title = "Somewhere over the rainbow"');
            isTrue(foo.name.wasCalled(2), 'callback was called twice.');
            equal(foo.name.getCalledCount(), 2, 'callback was called twice.');
            var info1 = foo.name.getData(0);
            isTrue(info1.argsPassed[0] === 'Somewhere over ', 'callback was passed "Somewhere over"');
            isTrue(info1.returned === 'Somewhere over ', 'callback returned "Somewhere over "');
            isTrue(info1.context === foo, 'foo.name was called the 1st time with the foo context');
            var info2 = foo.name.getData(1);
            isTrue(info2.argsPassed[0] === 'the rainbow', 'callback was passed "the rainbow"');
            isTrue(info2.returned === 'the rainbow', 'callback returned "the rainbow"');
            isTrue(info2.context === foo, 'foo.name was called the 2nd time with the foo context');
        });
    });
});
<|endoftext|>


<commit_before>import React from 'react';
import PropTypes from 'prop-types';
import {QueryRenderer, graphql} from 'react-relay';

import {autobind} from '../helpers';
import {SearchPropType, RemotePropType, BranchSetPropType} from '../prop-types';
import IssueishListController, {BareIssueishListController} from '../controllers/issueish-list-controller';
import RelayNetworkLayerManager from '../relay-network-layer-manager';

export default class IssueishListContainer extends React.Component {
  static propTypes = {
    token: PropTypes.string.isRequired,
    host: PropTypes.string.isRequired,

    repository: PropTypes.shape({
      defaultBranchRef: PropTypes.string,
    }),

    search: SearchPropType.isRequired,
    remote: RemotePropType.isRequired,
    branches: BranchSetPropType.isRequired,
    aheadCount: PropTypes.number.isRequired,
    pushInProgress: PropTypes.bool.isRequired,

    onCreatePr: PropTypes.func.isRequired,
  }

  constructor(props) {
    super(props);

    autobind(this, 'renderQueryResult');
  }

  render() {
    const environment = RelayNetworkLayerManager.getEnvironmentForHost(this.props.host, this.props.token);

    if (this.props.search.isNull()) {
      return (
        <BareIssueishListController
          repository={this.props.repository}

          search={this.props.search}
          remote={this.props.remote}
          branches={this.props.branches}
          aheadCount={this.props.aheadCount}
          pushInProgress={this.props.pushInProgress}
          isLoading={false}

          onCreatePr={this.props.onCreatePr}
        />
      );
    }

    const query = graphql`
      query issueishListContainerQuery($query: String!) {
        search(first: 20, query: $query, type: ISSUE) {
          ...issueishListController_results
        }
      }
    `;
    const variables = {
      query: this.props.search.createQuery(),
    };

    return (
      <QueryRenderer
        environment={environment}
        variables={variables}
        query={query}
        render={this.renderQueryResult}
      />
    );
  }

  renderQueryResult({error, props}) {
    if (error) {
      return null;
    }

    if (props === null) {
      return (
        <BareIssueishListController
          repository={this.props.repository}

          search={this.props.search}
          remote={this.props.remote}
          branches={this.props.branches}
          aheadCount={this.props.aheadCount}
          pushInProgress={this.props.pushInProgress}
          isLoading={true}

          onCreatePr={this.props.onCreatePr}
        />
      );
    }

    return (
      <IssueishListController
        results={props.search}
        repository={this.props.repository}

        search={this.props.search}
        remote={this.props.remote}
        branches={this.props.branches}
        aheadCount={this.props.aheadCount}
        pushInProgress={this.props.pushInProgress}
        isLoading={true}

        onCreatePr={this.props.onCreatePr}
      />
    );
  }
}
<commit_msg>Pass isLoading=false when not loading<commit_after>import React from 'react';
import PropTypes from 'prop-types';
import {QueryRenderer, graphql} from 'react-relay';

import {autobind} from '../helpers';
import {SearchPropType, RemotePropType, BranchSetPropType} from '../prop-types';
import IssueishListController, {BareIssueishListController} from '../controllers/issueish-list-controller';
import RelayNetworkLayerManager from '../relay-network-layer-manager';

export default class IssueishListContainer extends React.Component {
  static propTypes = {
    token: PropTypes.string.isRequired,
    host: PropTypes.string.isRequired,

    repository: PropTypes.shape({
      defaultBranchRef: PropTypes.string,
    }),

    search: SearchPropType.isRequired,
    remote: RemotePropType.isRequired,
    branches: BranchSetPropType.isRequired,
    aheadCount: PropTypes.number.isRequired,
    pushInProgress: PropTypes.bool.isRequired,

    onCreatePr: PropTypes.func.isRequired,
  }

  constructor(props) {
    super(props);

    autobind(this, 'renderQueryResult');
  }

  render() {
    const environment = RelayNetworkLayerManager.getEnvironmentForHost(this.props.host, this.props.token);

    if (this.props.search.isNull()) {
      return (
        <BareIssueishListController
          repository={this.props.repository}

          search={this.props.search}
          remote={this.props.remote}
          branches={this.props.branches}
          aheadCount={this.props.aheadCount}
          pushInProgress={this.props.pushInProgress}
          isLoading={false}

          onCreatePr={this.props.onCreatePr}
        />
      );
    }

    const query = graphql`
      query issueishListContainerQuery($query: String!) {
        search(first: 20, query: $query, type: ISSUE) {
          ...issueishListController_results
        }
      }
    `;
    const variables = {
      query: this.props.search.createQuery(),
    };

    return (
      <QueryRenderer
        environment={environment}
        variables={variables}
        query={query}
        render={this.renderQueryResult}
      />
    );
  }

  renderQueryResult({error, props}) {
    if (error) {
      return null;
    }

    if (props === null) {
      return (
        <BareIssueishListController
          repository={this.props.repository}

          search={this.props.search}
          remote={this.props.remote}
          branches={this.props.branches}
          aheadCount={this.props.aheadCount}
          pushInProgress={this.props.pushInProgress}
          isLoading={true}

          onCreatePr={this.props.onCreatePr}
        />
      );
    }

    return (
      <IssueishListController
        results={props.search}
        repository={this.props.repository}

        search={this.props.search}
        remote={this.props.remote}
        branches={this.props.branches}
        aheadCount={this.props.aheadCount}
        pushInProgress={this.props.pushInProgress}
        isLoading={false}

        onCreatePr={this.props.onCreatePr}
      />
    );
  }
}
<|endoftext|>


<commit_before>const _ = require('lodash')
const BaseChecker = require('./../base-checker')

const DEFAULT_MAX_CHARACTERS_LONG = 20

const ruleId = 'reason-string'
const meta = {
  type: 'best-practises',

  docs: {
    description:
      'Require or revert statement must have a reason string and check that each reason string is at most N characters long.',
    category: 'Best Practise Rules'
  },

  isDefault: false,
  recommended: false,
  defaultSetup: ['warn', { maxLength: DEFAULT_MAX_CHARACTERS_LONG }],

  schema: [
    {
      type: 'array',
      items: [
        {
          properties: {
            maxLength: {
              type: 'integer'
            }
          },
          additionalProperties: false
        }
      ],
      uniqueItems: true,
      minItems: 2
    }
  ]
}

class ReasonStringChecker extends BaseChecker {
  constructor(reporter, config) {
    super(reporter, ruleId, meta)

    this.maxCharactersLong =
      (config &&
        config.getObjectPropertyNumber(ruleId, 'maxLength', DEFAULT_MAX_CHARACTERS_LONG)) ||
      DEFAULT_MAX_CHARACTERS_LONG
  }

  enterFunctionCallArguments(ctx) {
    if (this.isReasonStringStatement(ctx)) {
      const functionParameters = this.getFunctionParameters(ctx)
      const functionName = this.getFunctionName(ctx)

      const lastFunctionParameter = _.last(functionParameters)

      const haveReasonMessage = this.haveReasonMessage(lastFunctionParameter)
      const haveNoMessage = functionParameters.length <= 1 || !haveReasonMessage
      if (haveNoMessage) {
        this._errorHaveNoMessage(ctx, functionName)
      } else if (haveReasonMessage) {
        const lastParameterWithoutQuotes = lastFunctionParameter.replace(/['"]+/g, '')
        if (lastParameterWithoutQuotes.length > this.maxCharactersLong) {
          this._errorMessageIsTooLong(ctx, functionName)
        }
      }
    }
  }

  isReasonStringStatement(ctx) {
    const name = this.getFunctionName(ctx)
    return name === 'revert' || name === 'require'
  }

  getFunctionName(ctx) {
    const parent = ctx.parentCtx
    const name = parent.children[0]
    return name.getText()
  }

  getFunctionParameters(ctx) {
    const parent = ctx.parentCtx
    const nodes = parent.children[2]
    const children = nodes.children[0].children
    const parameters = children
      .filter(value => value.getText() !== ',')
      .map(value => value.getText())
    return parameters
  }

  haveReasonMessage(str) {
    return str.indexOf("'") >= 0 || str.indexOf('"') >= 0
  }

  _errorHaveNoMessage(ctx, key) {
    this.error(ctx, `Provide an error message for ${key}`)
  }

  _errorMessageIsTooLong(ctx, key) {
    this.error(ctx, `Error message for ${key} is too long`)
  }
}

module.exports = ReasonStringChecker
<commit_msg>Update to use has instead of have<commit_after>const _ = require('lodash')
const BaseChecker = require('./../base-checker')

const DEFAULT_MAX_CHARACTERS_LONG = 20

const ruleId = 'reason-string'
const meta = {
  type: 'best-practises',

  docs: {
    description:
      'Require or revert statement must have a reason string and check that each reason string is at most N characters long.',
    category: 'Best Practise Rules'
  },

  isDefault: false,
  recommended: false,
  defaultSetup: ['warn', { maxLength: DEFAULT_MAX_CHARACTERS_LONG }],

  schema: [
    {
      type: 'array',
      items: [
        {
          properties: {
            maxLength: {
              type: 'integer'
            }
          },
          additionalProperties: false
        }
      ],
      uniqueItems: true,
      minItems: 2
    }
  ]
}

class ReasonStringChecker extends BaseChecker {
  constructor(reporter, config) {
    super(reporter, ruleId, meta)

    this.maxCharactersLong =
      (config &&
        config.getObjectPropertyNumber(ruleId, 'maxLength', DEFAULT_MAX_CHARACTERS_LONG)) ||
      DEFAULT_MAX_CHARACTERS_LONG
  }

  enterFunctionCallArguments(ctx) {
    if (this.isReasonStringStatement(ctx)) {
      const functionParameters = this.getFunctionParameters(ctx)
      const functionName = this.getFunctionName(ctx)

      const lastFunctionParameter = _.last(functionParameters)

      const hasReasonMessage = this.hasReasonMessage(lastFunctionParameter)
      const haveNoMessage = functionParameters.length <= 1 || !hasReasonMessage
      if (haveNoMessage) {
        this._errorHaveNoMessage(ctx, functionName)
      } else if (hasReasonMessage) {
        const lastParameterWithoutQuotes = lastFunctionParameter.replace(/['"]+/g, '')
        if (lastParameterWithoutQuotes.length > this.maxCharactersLong) {
          this._errorMessageIsTooLong(ctx, functionName)
        }
      }
    }
  }

  isReasonStringStatement(ctx) {
    const name = this.getFunctionName(ctx)
    return name === 'revert' || name === 'require'
  }

  getFunctionName(ctx) {
    const parent = ctx.parentCtx
    const name = parent.children[0]
    return name.getText()
  }

  getFunctionParameters(ctx) {
    const parent = ctx.parentCtx
    const nodes = parent.children[2]
    const children = nodes.children[0].children
    const parameters = children
      .filter(value => value.getText() !== ',')
      .map(value => value.getText())
    return parameters
  }

  hasReasonMessage(str) {
    return str.indexOf("'") >= 0 || str.indexOf('"') >= 0
  }

  _errorHaveNoMessage(ctx, key) {
    this.error(ctx, `Provide an error message for ${key}`)
  }

  _errorMessageIsTooLong(ctx, key) {
    this.error(ctx, `Error message for ${key} is too long`)
  }
}

module.exports = ReasonStringChecker
<|endoftext|>


<commit_before>var _ = require('lodash');
var moment = require('frozen-moment');
var periodTools = require('./periodTools');
var ressourcesMap = require('./ressources');
var individuSchema = require('./individu');
var menageSchema = require('./menage');
var familleSchema = require('./famille');
var foyerSchema = require('./foyerFiscal');
var common = require('./common');
var PRESTATIONS = require('../prestations');

var isNotValidValue = function(value) {
    return _.isNaN(value) || _.isUndefined(value) || value === null;
};

var getPeriods = function(dateDeValeur) {
    dateDeValeur = moment(dateDeValeur).freeze();
    return {
        thisMonth: {
            key: dateDeValeur.format('YYYY-MM')
        },
        '1MonthsAgo': {
            key: dateDeValeur.subtract(1, 'months').format('YYYY-MM')
        },
        '2MonthsAgo': {
            key: dateDeValeur.subtract(2, 'months').format('YYYY-MM')
        },
        '3MonthsAgo': {
            key: dateDeValeur.subtract(3, 'months').format('YYYY-MM')
        },
        // tableaux contenant les douze derniers mois (pris individuellement)
        last12Months: _.map(_.range(1, 12 + 1), function(monthIndex) {
            return {
                key: dateDeValeur.subtract(monthIndex, 'months').format('YYYY-MM')
            };
        }),
       rollingYear: {
            start: dateDeValeur.subtract(1, 'years').subtract(1, 'months'),
            end: dateDeValeur,
            key: 'year:' + dateDeValeur.subtract(1, 'years').format('YYYY-MM')
        },
        anneeFiscaleReference: {
            key: dateDeValeur.subtract(2, 'years').year()
        },
        rollingTrimester: {
            start: dateDeValeur.subtract(3, 'months').subtract(1, 'months'),
            end: dateDeValeur,
            key: dateDeValeur.subtract(3, 'months').format('YYYY-MM') + ':3'
        }
    };
};

var applyRessources = function(mesAidesEntity, openfiscaEntity, mappingSchema, situation) {
    var dateDeValeur = situation.dateDeValeur;
    var periods = getPeriods(dateDeValeur);

    function isPeriodWithin(agregatKey, periode) {
        return periode.isAfter(periods[agregatKey].start, 'month') && periode.isBefore(periods[agregatKey].end, 'month');
    }

    var ressourcesByType = _.groupBy(mesAidesEntity.ressources, 'type');

    _.forEach(mappingSchema, function(definitions, openfiscaKey) {
        if (! _.isArray(definitions)) {
            definitions = [definitions];
        }
        _.forEach(definitions, function(definition) {

            var srcKey = definition.src || definition;
            var fn = definition.fn;

            if (ressourcesByType[srcKey]) {
                var result = openfiscaEntity[openfiscaKey] || {};

                _.forEach(ressourcesByType[srcKey], function(item) {
                    var montant = fn ? fn(item.montant) : item.montant;
                    if (item.periode) {
                        // Période d'origine
                        if (! result[item.periode]) {
                            result[item.periode] = 0;
                        }
                        result[item.periode] += montant;

                        // Aggrégats
                        _.keys(periods).forEach(function (agregatKey) {
                            if (agregatKey != 'anneeFiscaleReference' && isPeriodWithin(agregatKey, moment(item.periode, 'YYYY-MM'))) {
                                var periodKey = periods[agregatKey].key;
                                if (! result[periodKey]) {
                                    result[periodKey] = 0;
                                }
                                result[periodKey] += montant;
                            }
                        });
                    }
                });

                // On prolonge la ressource perçue quand c'est possible
                var ressourceLastMonth = _.find(ressourcesByType[srcKey], {periode : periods['1MonthsAgo'].key});
                if (ressourceLastMonth && ressourceLastMonth.montant && !_.contains(mesAidesEntity.interruptedRessources, srcKey)) {
                    result[periods.thisMonth.key] = result[periods.thisMonth.key] || 0;
                    result[periods.thisMonth.key] += fn ? fn(ressourceLastMonth.montant) : ressourceLastMonth.montant;
                }

                openfiscaEntity[openfiscaKey] = result;

                // On suppose que les revenus pour l'année fiscale de référence sont les mêmes que pour les 12 derniers mois, si ces revenus n'ont pas encore
                // été renseignés
                var rollingYearKey = periods.rollingYear.key,
                    anneeFiscaleReferenceKey = periods.anneeFiscaleReference.key;
                if (! situation.ressourcesYearMoins2Captured) {
                    openfiscaEntity[openfiscaKey][anneeFiscaleReferenceKey] = openfiscaEntity[openfiscaKey][rollingYearKey];
                }
            }
        });
    });
};

var setNonInjectedPrestationsToZero = function(famille, individus, dateDeValeur) {
    var periods = getPeriods(dateDeValeur);
    var targetPeriods = periods.last12Months.concat([periods.rollingYear]);
    var prestationsFinancieres = _.pick(PRESTATIONS, function(definition) {
        return definition.type == Number;
    });
    _.forEach(targetPeriods, function(period) {
        _.forEach(prestationsFinancieres, function(definition, prestationName) {
            if (definition.prestationIndividuelle) {
                _.forEach(individus, function(individu) {
                    individu[prestationName] = individu[prestationName] || {};
                    individu[prestationName][period.key] = individu[prestationName][period.key] || 0;
                });
            } else {
                famille[prestationName] = famille[prestationName] || {};
                famille[prestationName][period.key] = famille[prestationName][period.key] || 0;
            }
        });
    });
};

function buildOpenFiscaEntity (mesAidesEntity, mappingSchema, situation) {
    var periods = getPeriods(situation.dateDeValeur);
    var monthKeys = ['thisMonth', '1MonthsAgo', '2MonthsAgo', '3MonthsAgo'].map(function (month) {
        return periods[month].key;
    });

    var result = {};
    _.forEach(mappingSchema, function(definition, openfiscaKey) {
        var params = _.isString(definition) ? { src: definition } : definition;

        if (params.values) {
            result[openfiscaKey] = params.values[mesAidesEntity[params.src]];
        } else if (params.fn) {
            result[openfiscaKey] = params.src ? params.fn(mesAidesEntity[params.src], mesAidesEntity, situation) : params.fn(mesAidesEntity, situation);
        } else {
            result[openfiscaKey] = mesAidesEntity[params.src];
        }

        if (params.round && _.isNumber(result[openfiscaKey])) {
            result[openfiscaKey] = Math.round(result[openfiscaKey]);
        }

        // Supprime les null (mal supportés par OpenFisca)
        if (isNotValidValue(result[openfiscaKey])) {
            if ('default' in params) result[openfiscaKey] = params.default;
            else delete result[openfiscaKey];
        }

        // On étend la valeur aux 3 derniers mois, succeptibles d'être appelés par le RSA et la PPA dans Openfisca
        var value = result[openfiscaKey];
        if (_.isUndefined(value) || params.copyTo3PreviousMonths === false) {
            return result;
        }
        result[openfiscaKey] = {};
        monthKeys.forEach(function(monthKey) {
            result[openfiscaKey][monthKey] = value;
        });
    });
    return result;
}

var mapIndividus = exports.mapIndividus = function (situation) {
    var individus = _.filter(situation.individus, function(individu) {
        return common.isIndividuValid(individu, situation);
    });

    return _.map(individus, function(individu) {
        if (individu.role === 'demandeur') {
            _.extend(individu, situation.patrimoine);

        }
        var openfiscaIndividu = buildOpenFiscaEntity(individu, individuSchema, situation);
        applyRessources(individu, openfiscaIndividu, ressourcesMap.individu, situation);
        return openfiscaIndividu;
    });
};

var mapFamille = function(situation) {
    var famille = buildOpenFiscaEntity(situation, familleSchema, situation);
    var ressources = [];
    _.forEach(situation.individus, function(individu) {
        ressources = ressources.concat(individu.ressources);
    });
    applyRessources({ ressources: ressources }, famille, ressourcesMap.famille, situation);

    return famille;
};

var mapFoyerFiscal = exports.mapFoyerFiscal = function(situation) {
    var foyerFiscal = buildOpenFiscaEntity(situation, foyerSchema, situation);
    var ressources = [];
    _.forEach(situation.individus, function(individu) {
        ressources = ressources.concat(individu.ressources);
    });
    applyRessources({ ressources: ressources }, foyerFiscal, ressourcesMap.foyerFiscal, situation);

    return foyerFiscal;
};

exports.buildOpenFiscaRequest = function(situation, debug) {
    var famille = mapFamille(situation),
        individus = mapIndividus(situation),
        foyerFiscal = mapFoyerFiscal(situation);

    setNonInjectedPrestationsToZero(famille, individus, situation.dateDeValeur);

    return {
        intermediate_variables: debug,
        labels: true,
        scenarios: [{
            test_case: {
                familles: [ famille ],
                foyers_fiscaux: [ foyerFiscal ],
                individus: individus,
                menages: [ buildOpenFiscaEntity(situation, menageSchema, situation) ]
            },
            period: 'month:' + periodTools.toOpenFiscaFormat(situation.dateDeValeur)
        }],
        variables: _.keys(PRESTATIONS)
    };
};
<commit_msg>Do not give aggregated values to OpenFisca<commit_after>var _ = require('lodash');
var moment = require('frozen-moment');
var periodTools = require('./periodTools');
var ressourcesMap = require('./ressources');
var individuSchema = require('./individu');
var menageSchema = require('./menage');
var familleSchema = require('./famille');
var foyerSchema = require('./foyerFiscal');
var common = require('./common');
var PRESTATIONS = require('../prestations');

var isNotValidValue = function(value) {
    return _.isNaN(value) || _.isUndefined(value) || value === null;
};

var getPeriods = function(dateDeValeur) {
    dateDeValeur = moment(dateDeValeur).freeze();
    return {
        thisMonth: {
            key: dateDeValeur.format('YYYY-MM')
        },
        '1MonthsAgo': {
            key: dateDeValeur.subtract(1, 'months').format('YYYY-MM')
        },
        '2MonthsAgo': {
            key: dateDeValeur.subtract(2, 'months').format('YYYY-MM')
        },
        '3MonthsAgo': {
            key: dateDeValeur.subtract(3, 'months').format('YYYY-MM')
        },
        // tableaux contenant les douze derniers mois (pris individuellement)
        last12Months: _.map(_.range(1, 12 + 1), function(monthIndex) {
            return {
                key: dateDeValeur.subtract(monthIndex, 'months').format('YYYY-MM')
            };
        }),
        anneeFiscaleReference: {
            key: dateDeValeur.subtract(2, 'years').year()
        },
    };
};

var applyRessources = function(mesAidesEntity, openfiscaEntity, mappingSchema, situation) {
    var dateDeValeur = situation.dateDeValeur;
    var periods = getPeriods(dateDeValeur);
    var ressourcesByType = _.groupBy(mesAidesEntity.ressources, 'type');

    _.forEach(mappingSchema, function(definitions, openfiscaKey) {
        if (! _.isArray(definitions)) {
            definitions = [definitions];
        }
        _.forEach(definitions, function(definition) {

            var srcKey = definition.src || definition;
            var fn = definition.fn;

            if (ressourcesByType[srcKey]) {
                var result = openfiscaEntity[openfiscaKey] || {};

                _.forEach(ressourcesByType[srcKey], function(item) {
                    var montant = fn ? fn(item.montant) : item.montant;
                    if (item.periode) {
                        // Période d'origine
                        if (! result[item.periode]) {
                            result[item.periode] = 0;
                        }
                        result[item.periode] += montant;
                    }
                });

                // On prolonge la ressource perçue quand c'est possible
                var ressourceLastMonth = _.find(ressourcesByType[srcKey], {periode : periods['1MonthsAgo'].key});
                if (ressourceLastMonth && ressourceLastMonth.montant && !_.contains(mesAidesEntity.interruptedRessources, srcKey)) {
                    result[periods.thisMonth.key] = result[periods.thisMonth.key] || 0;
                    result[periods.thisMonth.key] += fn ? fn(ressourceLastMonth.montant) : ressourceLastMonth.montant;
                }

                // On suppose que les revenus pour l'année fiscale de référence sont les mêmes que pour les 12 derniers mois, si ces revenus n'ont pas encore
                // été renseignés
                if (! situation.ressourcesYearMoins2Captured) {
                    result[periods.anneeFiscaleReference.key] = periods.last12Months.reduce(function(sum, periodObject) {
                        return sum + result[periodObject.key];
                    }, 0);
                }

                openfiscaEntity[openfiscaKey] = result;
            }
        });
    });
};

var setNonInjectedPrestationsToZero = function(famille, individus, dateDeValeur) {
    var periods = getPeriods(dateDeValeur);
    var targetPeriods = periods.last12Months;
    var prestationsFinancieres = _.pick(PRESTATIONS, function(definition) {
        return definition.type == Number;
    });
    _.forEach(targetPeriods, function(period) {
        _.forEach(prestationsFinancieres, function(definition, prestationName) {
            if (definition.prestationIndividuelle) {
                _.forEach(individus, function(individu) {
                    individu[prestationName] = individu[prestationName] || {};
                    individu[prestationName][period.key] = individu[prestationName][period.key] || 0;
                });
            } else {
                famille[prestationName] = famille[prestationName] || {};
                famille[prestationName][period.key] = famille[prestationName][period.key] || 0;
            }
        });
    });
};

function buildOpenFiscaEntity (mesAidesEntity, mappingSchema, situation) {
    var periods = getPeriods(situation.dateDeValeur);
    var monthKeys = ['thisMonth', '1MonthsAgo', '2MonthsAgo', '3MonthsAgo'].map(function (month) {
        return periods[month].key;
    });

    var result = {};
    _.forEach(mappingSchema, function(definition, openfiscaKey) {
        var params = _.isString(definition) ? { src: definition } : definition;

        if (params.values) {
            result[openfiscaKey] = params.values[mesAidesEntity[params.src]];
        } else if (params.fn) {
            result[openfiscaKey] = params.src ? params.fn(mesAidesEntity[params.src], mesAidesEntity, situation) : params.fn(mesAidesEntity, situation);
        } else {
            result[openfiscaKey] = mesAidesEntity[params.src];
        }

        if (params.round && _.isNumber(result[openfiscaKey])) {
            result[openfiscaKey] = Math.round(result[openfiscaKey]);
        }

        // Supprime les null (mal supportés par OpenFisca)
        if (isNotValidValue(result[openfiscaKey])) {
            if ('default' in params) result[openfiscaKey] = params.default;
            else delete result[openfiscaKey];
        }

        // On étend la valeur aux 3 derniers mois, succeptibles d'être appelés par le RSA et la PPA dans Openfisca
        var value = result[openfiscaKey];
        if (_.isUndefined(value) || params.copyTo3PreviousMonths === false) {
            return result;
        }
        result[openfiscaKey] = {};
        monthKeys.forEach(function(monthKey) {
            result[openfiscaKey][monthKey] = value;
        });
    });
    return result;
}

var mapIndividus = exports.mapIndividus = function (situation) {
    var individus = _.filter(situation.individus, function(individu) {
        return common.isIndividuValid(individu, situation);
    });

    return _.map(individus, function(individu) {
        if (individu.role === 'demandeur') {
            _.extend(individu, situation.patrimoine);

        }
        var openfiscaIndividu = buildOpenFiscaEntity(individu, individuSchema, situation);
        applyRessources(individu, openfiscaIndividu, ressourcesMap.individu, situation);
        return openfiscaIndividu;
    });
};

var mapFamille = function(situation) {
    var famille = buildOpenFiscaEntity(situation, familleSchema, situation);
    var ressources = [];
    _.forEach(situation.individus, function(individu) {
        ressources = ressources.concat(individu.ressources);
    });
    applyRessources({ ressources: ressources }, famille, ressourcesMap.famille, situation);

    return famille;
};

var mapFoyerFiscal = exports.mapFoyerFiscal = function(situation) {
    var foyerFiscal = buildOpenFiscaEntity(situation, foyerSchema, situation);
    var ressources = [];
    _.forEach(situation.individus, function(individu) {
        ressources = ressources.concat(individu.ressources);
    });
    applyRessources({ ressources: ressources }, foyerFiscal, ressourcesMap.foyerFiscal, situation);

    return foyerFiscal;
};

exports.buildOpenFiscaRequest = function(situation, debug) {
    var famille = mapFamille(situation),
        individus = mapIndividus(situation),
        foyerFiscal = mapFoyerFiscal(situation);

    setNonInjectedPrestationsToZero(famille, individus, situation.dateDeValeur);

    return {
        intermediate_variables: debug,
        labels: true,
        scenarios: [{
            test_case: {
                familles: [ famille ],
                foyers_fiscaux: [ foyerFiscal ],
                individus: individus,
                menages: [ buildOpenFiscaEntity(situation, menageSchema, situation) ]
            },
            period: 'month:' + periodTools.toOpenFiscaFormat(situation.dateDeValeur)
        }],
        variables: _.keys(PRESTATIONS)
    };
};
<|endoftext|>


<commit_before>export default function(val) {
  return valExp => {
    let res;
    if (valExp instanceof RegExp) res = valExp.test(val || '');
    else if (typeof valExp === 'string')
      res = (val || '').trim() === valExp.trim();
    else if (typeof valExp === 'number') res = +val === valExp;
    else if (typeof valExp === 'boolean') res = !!val === valExp;
    return +res * 100;
  };
}
<commit_msg>Equals accepts an array of possible values<commit_after>export default function(val) {
  return exps => {
    if (!Array.isArray(exps)) exps = [exps];
    return (
      +exps.some(valExp => {
        if (valExp instanceof RegExp) return valExp.test(val || '');
        if (typeof valExp === 'string')
          return (val || '').trim() === valExp.trim();
        if (typeof valExp === 'number') return +val === valExp;
        if (typeof valExp === 'boolean') return !!val === valExp;
        return false;
      }) * 100
    );
  };
}
<|endoftext|>


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

/**
 * Delete record by primary key. Relies on class destructors to initiate any changes that are
 * required for clean up.
 *
 * @param   {Realm}   database
 * @param   {string}  recordType       Internal record type.
 * @param   {string}  primaryKey       The primary key of the database object, usually an id field.
 * @param   {string}  primaryKeyField  The field used as the primary key, defaults to 'id'.
 */
export const deleteRecord = (database, recordType, primaryKey, primaryKeyField = 'id') => {
  // 'delete' is a reserved word, |deleteRecord| is in the upper scope.
  const obliterate = () => {
    const deleteResults = database
      .objects(recordType)
      .filtered(`${primaryKeyField} == $0`, primaryKey);
    if (deleteResults && deleteResults.length > 0) {
      database.delete(recordType, deleteResults[0]);
    }
  };

  switch (recordType) {
    case 'NameTag':
    case 'NameTagJoin':
    case 'Item':
    case 'ItemBatch':
    case 'ItemCategory':
    case 'ItemDepartment':
    case 'ItemStoreJoin':
    case 'MasterList':
    case 'MasterListItem':
    case 'Name':
    case 'NameStoreJoin':
    case 'NumberSequence':
    case 'NumberToReuse':
    case 'Prescriber':
    case 'Requisition':
    case 'RequisitionItem':
    case 'Stocktake':
    case 'StocktakeBatch':
    case 'Transaction':
    case 'TransactionBatch':
    case 'TransactionCategory': {
      obliterate();
      break;
    }

    // Local list item is mimicked with master list item..
    case 'LocalListItem':
      deleteRecord(database, 'MasterListItem', primaryKey, primaryKeyField);
      break;
    case 'MasterListNameJoin': {
      // Joins for local lists are mapped to and mimicked by a masterlist of the same id.
      const masterList = database.objects('MasterList').filtered('id == $0', primaryKey)[0];
      if (masterList) {
        // Local list, delete the masterlist previously created for it.
        deleteRecord(database, 'MasterList', primaryKey, primaryKeyField);
      } else {
        // Delete the 'MasterListNameJoin' as per the usual case.
        obliterate();
      }
      break;
    }
    default:
      break; // Silently ignore record types which should not be synced to mobile.
  }
};

export default deleteRecord;
<commit_msg>Add delete any records without having to specify object types<commit_after>/**
 * mSupply Mobile
 * Sustainable Solutions (NZ) Ltd. 2019
 */

/**
 * Delete record by primary key. Relies on class destructors to initiate any changes that are
 * required for clean up.
 *
 * @param   {Realm}   database
 * @param   {string}  recordType       Internal record type.
 * @param   {string}  primaryKey       The primary key of the database object, usually an id field.
 * @param   {string}  primaryKeyField  The field used as the primary key, defaults to 'id'.
 */
export const deleteRecord = (database, recordType, primaryKey, primaryKeyField = 'id') => {
  // 'delete' is a reserved word, |deleteRecord| is in the upper scope.
  const obliterate = () => {
    try {
      const deleteResults = database
        .objects(recordType)
        .filtered(`${primaryKeyField} == $0`, primaryKey);
      if (deleteResults && deleteResults.length > 0) {
        database.delete(recordType, deleteResults[0]);
      }
    } catch {
      // .objects will throw if the record type is not a valid type within the schema.
      // This could happen by receiving delete sync records from the server which are
      // not a part of mSupply mobile. For example, system data which is not used. It is
      // rare, but in this case silently ignore record types which should not be synced to mobile.
    }
  };

  switch (recordType) {
    // Local list item is mimicked with master list item..
    case 'LocalListItem':
      deleteRecord(database, 'MasterListItem', primaryKey, primaryKeyField);
      break;
    case 'MasterListNameJoin': {
      // Joins for local lists are mapped to and mimicked by a masterlist of the same id.
      const masterList = database.objects('MasterList').filtered('id == $0', primaryKey)[0];
      if (masterList) {
        // Local list, delete the masterlist previously created for it.
        deleteRecord(database, 'MasterList', primaryKey, primaryKeyField);
      } else {
        // Delete the 'MasterListNameJoin' as per the usual case.
        obliterate();
      }
      break;
    }
    default:
      obliterate();
  }
};

export default deleteRecord;
<|endoftext|>


<commit_before>'use strict';

describe('datepicker', function() {

  var $compile, $templateCache, $datepicker, scope, sandboxEl, today;

  beforeEach(module('ngSanitize'));
  beforeEach(module('mgcrea.ngStrap.datepicker'));

  beforeEach(inject(function (_$rootScope_, _$compile_, _$templateCache_, _$datepicker_) {
    scope = _$rootScope_.$new();
    sandboxEl = $('
').attr('id', 'sandbox').appendTo($('body'));
    $compile = _$compile_;
    $templateCache = _$templateCache_;
    $datepicker = _$datepicker_;
    today = new Date();
  }));

  afterEach(function() {
    scope.$destroy();
    sandboxEl.remove();
  });

  // Templates

  var templates = {
    'default': {
      scope: {selectedDate: new Date()},
      element: '<input type="text" ng-model="selectedDate" bs-datepicker>'
    },
    'value-undefined': {
      element: '<input type="text" ng-model="selectedUndefined" bs-datepicker>'
    },
    'value-past': {
      scope: {selectedDate: new Date(Date.UTC(1986, 1, 22))},
      element: '<input type="text" ng-model="selectedDate" bs-datepicker>'
    },
    'markup-ngRepeat': {
      element: '

<input type="text" ng-model="selectedDate" bs-datepicker>

'
    },
    'options-animation': {
      element: '

'
    },
    'options-placement': {
      element: '

'
    },
    'options-placement-exotic': {
      element: '

'
    },
    'options-trigger': {
      element: '

'
    },
    'options-template': {
      element: '<input type="text" data-template="custom" ng-model="selectedDate" bs-datepicker>'
    },
    'options-dateFormat': {
      scope: {selectedDate: new Date(Date.UTC(1986, 1, 22))},
      element: '<input type="text" ng-model="selectedDate" data-date-format="yyyy-MM-dd" bs-datepicker>'
    },
    'options-minDate': {
      scope: {selectedDate: new Date('02/22/86'), minDate: '02/20/86'},
      element: '<input type="text" ng-model="selectedDate" data-min-date="{{minDate}}" bs-datepicker>'
    },
    'options-minDate-today': {
      scope: {},
      element: '<input type="text" ng-model="selectedDate" data-min-date="today" bs-datepicker>'
    },
    'options-maxDate-today': {
      scope: {},
      element: '<input type="text" ng-model="selectedDate" data-max-date="today" bs-datepicker>'
    },
    'options-maxDate': {
      scope: {selectedDate: new Date('02/22/86'), maxDate: '02/24/86'},
      element: '<input type="text" ng-model="selectedDate" data-max-date="{{maxDate}}" bs-datepicker>'
    },
    'options-autoclose': {
      element: '<input type="text" ng-model="selectedDate" data-autoclose="1" bs-datepicker>'
    },
    'options-useNative': {
      element: '<input type="text" ng-model="selectedDate" data-use-native="1" bs-datepicker>'
    }
  };

  function compileDirective(template, locals) {
    template = templates[template];
    angular.extend(scope, template.scope || templates['default'].scope, locals);
    var element = $(template.element).appendTo(sandboxEl);
    element = $compile(element)(scope);
    scope.$digest();
    return jQuery(element[0]);
  }

  // Tests

  describe('with default template', function() {

    it('should open on focus', function() {
      var elm = compileDirective('default');
      expect(sandboxEl.children('.dropdown-menu.datepicker').length).toBe(0);
      angular.element(elm[0]).triggerHandler('focus');
      expect(sandboxEl.children('.dropdown-menu.datepicker').length).toBe(1);
    });

    it('should close on blur', function() {
      var elm = compileDirective('default');
      expect(sandboxEl.children('.dropdown-menu.datepicker').length).toBe(0);
      angular.element(elm[0]).triggerHandler('focus');
      angular.element(elm[0]).triggerHandler('blur');
      expect(sandboxEl.children('.dropdown-menu.datepicker').length).toBe(0);
    });

    it('should correctly compile inner content', function() {
      var elm = compileDirective('default');
      angular.element(elm[0]).triggerHandler('focus');
      expect(sandboxEl.find('.dropdown-menu tbody td').length).toBe(5);
      expect(sandboxEl.find('.dropdown-menu tbody .btn').length).toBe(7 * 5);
    });

    it('should correctly support undefined values', function() {
      var elm = compileDirective('value-undefined');
      angular.element(elm[0]).triggerHandler('focus');
      expect(sandboxEl.find('.dropdown-menu tbody td').length).toBe(5);
      expect(sandboxEl.find('.dropdown-menu tbody .btn').length).toBe(7 * 5);
    });

    // iit('should only build the datepicker once', function() {
    //   var elm = compileDirective('value-past');
    //   angular.element(elm[0]).triggerHandler('focus');
    // });

    it('should correctly display active date', function() {
      var elm = compileDirective('default');
      angular.element(elm[0]).triggerHandler('focus');
      expect(sandboxEl.find('.dropdown-menu tbody td .btn-primary').text().trim() * 1).toBe(today.getUTCDate());
    });

    it('should support ngRepeat markup', function() {
      var elm = compileDirective('markup-ngRepeat');
      angular.element(elm.find('[bs-datepicker]:eq(0)')).triggerHandler('focus');
      expect(sandboxEl.find('.dropdown-menu tbody td').length).toBe(5);
      expect(sandboxEl.find('.dropdown-menu tbody .btn').length).toBe(7 * 5);
    });

  });

  // describe('using service', function() {

  //   it('should correctly open on next digest', function() {
  //     var myModal = $modal(templates['default'].scope.modal);
  //     scope.$digest();
  //     expect(bodyEl.children('.modal').length).toBe(1);
  //     myModal.hide();
  //     expect(bodyEl.children('.modal').length).toBe(0);
  //   });

  //   it('should correctly be destroyed', function() {
  //     var myModal = $modal(angular.extend(templates['default'].scope.modal));
  //     scope.$digest();
  //     expect(bodyEl.children('.modal').length).toBe(1);
  //     myModal.destroy();
  //     expect(bodyEl.children('.modal').length).toBe(0);
  //     expect(bodyEl.children().length).toBe(1);
  //   });

  //   it('should correctly work with ngClick', function() {
  //     var elm = compileDirective('markup-ngClick-service');
  //     var myModal = $modal(angular.extend({show: false}, templates['default'].scope.modal));
  //     scope.showModal = function() {
  //       myModal.$promise.then(myModal.show);
  //     };
  //     expect(bodyEl.children('.modal').length).toBe(0);
  //     angular.element(elm[0]).triggerHandler('click');
  //     expect(bodyEl.children('.modal').length).toBe(1);
  //   });

  // });

  describe('options', function() {

    describe('animation', function() {

      it('should default to `animation-fade` animation', function() {
        var elm = compileDirective('default');
        angular.element(elm[0]).triggerHandler('focus');
        expect(sandboxEl.children('.dropdown-menu').hasClass('animation-fade')).toBeTruthy();
      });

      it('should support custom animation', function() {
        var elm = compileDirective('options-animation');
        angular.element(elm[0]).triggerHandler('focus');
        expect(sandboxEl.children('.dropdown-menu').hasClass('animation-flipX')).toBeTruthy();
      });

    });

    describe('autoclose', function() {

      it('should close on select', function() {
        var elm = compileDirective('options-autoclose');
        expect(sandboxEl.children('.dropdown-menu.datepicker').length).toBe(0);
        angular.element(elm[0]).triggerHandler('focus');
        angular.element(sandboxEl.find('.dropdown-menu tbody .btn:first')).triggerHandler('click');
        expect(sandboxEl.children('.dropdown-menu.datepicker').length).toBe(0);
      });

    });

    describe('placement', function() {

      it('should default to `bottom-left` placement', function() {
        var elm = compileDirective('default');
        angular.element(elm[0]).triggerHandler('focus');
        expect(sandboxEl.children('.dropdown-menu').hasClass('bottom-left')).toBeTruthy();
      });

      it('should support placement', function() {
        var elm = compileDirective('options-placement');
        angular.element(elm[0]).triggerHandler('focus');
        expect(sandboxEl.children('.dropdown-menu').hasClass('bottom')).toBeTruthy();
      });

      it('should support exotic-placement', function() {
        var elm = compileDirective('options-placement-exotic');
        angular.element(elm[0]).triggerHandler('focus');
        expect(sandboxEl.children('.dropdown-menu').hasClass('bottom-right')).toBeTruthy();
      });

    });

    describe('trigger', function() {

      it('should support an alternative trigger', function() {
        var elm = compileDirective('options-trigger');
        expect(sandboxEl.children('.dropdown-menu').length).toBe(0);
        angular.element(elm[0]).triggerHandler('mouseenter');
        expect(sandboxEl.children('.dropdown-menu').length).toBe(1);
        angular.element(elm[0]).triggerHandler('mouseleave');
        expect(sandboxEl.children('.dropdown-menu').length).toBe(0);
      });

    });

    describe('template', function() {

      it('should support custom template', function() {
        $templateCache.put('custom', '

foo: {{selectedDate}}

');
        var elm = compileDirective('options-template');
        angular.element(elm[0]).triggerHandler('focus');
        expect(sandboxEl.find('.dropdown-inner').text()).toBe('foo: "' + scope.selectedDate.toISOString() + '"');
      });

      it('should support template with ngRepeat', function() {
        $templateCache.put('custom', '

{{i}}

');
        var elm = compileDirective('options-template');
        angular.element(elm[0]).triggerHandler('focus');
        expect(sandboxEl.find('.dropdown-inner').text()).toBe('123');
        // Consecutive toggles
        angular.element(elm[0]).triggerHandler('focus');
        angular.element(elm[0]).triggerHandler('blur');
        expect(sandboxEl.find('.dropdown-inner').text()).toBe('123');
      });

      it('should support template with ngClick', function() {
        $templateCache.put('custom', '

click me

');
        var elm = compileDirective('options-template');
        scope.dropdown = {counter: 0};
        angular.element(elm[0]).triggerHandler('focus');
        expect(angular.element(sandboxEl.find('.dropdown-inner > .btn')[0]).triggerHandler('click'));
        expect(scope.dropdown.counter).toBe(1);
        // Consecutive toggles
        angular.element(elm[0]).triggerHandler('focus');
        angular.element(elm[0]).triggerHandler('blur');
        expect(angular.element(sandboxEl.find('.dropdown-inner > .btn')[0]).triggerHandler('click'));
        expect(scope.dropdown.counter).toBe(2);
      });

    });

    describe('dateFormat', function() {

      it('should support a custom dateFormat', function() {
        var elm = compileDirective('options-dateFormat');
        expect(elm.val()).toBe('1986-02-22');
        angular.element(elm[0]).triggerHandler('focus');
        angular.element(sandboxEl.find('.dropdown-menu tbody .btn:first')).triggerHandler('click');
        expect(elm.val()).toBe('1986-01-25');
      });

    });

    describe('minDate', function() {

      it('should support a dynamic minDate', function() {
        var elm = compileDirective('options-minDate');
        angular.element(elm[0]).triggerHandler('focus');
        expect(sandboxEl.find('.dropdown-menu tbody button[disabled]').length).toBe(26);
        expect(sandboxEl.find('.dropdown-menu tbody button:contains(19)').is(':disabled')).toBeTruthy();
        expect(sandboxEl.find('.dropdown-menu tbody button:contains(20)').is(':disabled')).toBeFalsy();
        scope.minDate = '02/12/86';
        scope.$digest();
        expect(sandboxEl.find('.dropdown-menu tbody button[disabled]').length).toBe(18);
      });

      it('should support today as minDate', function() {
        var elm = compileDirective('options-minDate-today');
        angular.element(elm[0]).triggerHandler('focus');
        var date = new Date(), today = date.getDate();
        expect(sandboxEl.find('.dropdown-menu tbody button[disabled]').length > 0).toBeTruthy();
        expect(sandboxEl.find('.dropdown-menu tbody button:contains(' + today + ')').is(':disabled')).toBeFalsy();
      });

    });

    describe('maxDate', function() {

      it('should support a dynamic maxDate', function() {
        var elm = compileDirective('options-maxDate');
        angular.element(elm[0]).triggerHandler('focus');
        expect(sandboxEl.find('.dropdown-menu tbody button[disabled]').length).toBe(4);
        expect(sandboxEl.find('.dropdown-menu tbody button:contains(24)').is(':disabled')).toBeFalsy();
        expect(sandboxEl.find('.dropdown-menu tbody button:contains(25)').is(':disabled')).toBeTruthy();
        scope.maxDate = '02/12/86';
        scope.$digest();
        expect(sandboxEl.find('.dropdown-menu tbody button[disabled]').length).toBe(16);
      });

      it('should support today as maxDate', function() {
        var elm = compileDirective('options-maxDate-today');
        angular.element(elm[0]).triggerHandler('focus');
        var date = new Date(), today = date.getDate();
        expect(sandboxEl.find('.dropdown-menu tbody button[disabled]').length > 0).toBeTruthy();
        expect(sandboxEl.find('.dropdown-menu tbody button:contains(' + today + ')').is(':disabled')).toBeFalsy();
      });

    });

    describe('useNative', function() {

      it('should correctly compile template according to useNative', function() {
        var elm = compileDirective('options-useNative');
        angular.element(elm[0]).triggerHandler('focus');
        // @TODO ?
      });

    });

  });

});
<commit_msg>chore(datepicker): properly test against regression regarding issue #364<commit_after>'use strict';

describe('datepicker', function() {

  var $compile, $templateCache, $datepicker, dateFilter, scope, sandboxEl, today;

  beforeEach(module('ngSanitize'));
  beforeEach(module('mgcrea.ngStrap.datepicker'));

  beforeEach(inject(function (_$rootScope_, _$compile_, _$templateCache_, _$datepicker_, _dateFilter_) {
    scope = _$rootScope_.$new();
    sandboxEl = $('
').attr('id', 'sandbox').appendTo($('body'));
    $compile = _$compile_;
    $templateCache = _$templateCache_;
    $datepicker = _$datepicker_;
    dateFilter = _dateFilter_;
    today = new Date();
  }));

  afterEach(function() {
    scope.$destroy();
    sandboxEl.remove();
  });

  // Templates

  var templates = {
    'default': {
      scope: {selectedDate: new Date()},
      element: '<input type="text" ng-model="selectedDate" bs-datepicker>'
    },
    'value-undefined': {
      element: '<input type="text" ng-model="selectedUndefined" bs-datepicker>'
    },
    'value-past': {
      scope: {selectedDate: new Date(Date.UTC(1986, 1, 22))},
      element: '<input type="text" ng-model="selectedDate" bs-datepicker>'
    },
    'markup-ngRepeat': {
      element: '

<input type="text" ng-model="selectedDate" bs-datepicker>

'
    },
    'options-animation': {
      element: '

'
    },
    'options-placement': {
      element: '

'
    },
    'options-placement-exotic': {
      element: '

'
    },
    'options-trigger': {
      element: '

'
    },
    'options-template': {
      element: '<input type="text" data-template="custom" ng-model="selectedDate" bs-datepicker>'
    },
    'options-dateFormat': {
      scope: {selectedDate: new Date(Date.UTC(1986, 1, 22))},
      element: '<input type="text" ng-model="selectedDate" data-date-format="yyyy-MM-dd" bs-datepicker>'
    },
    'options-minDate': {
      scope: {selectedDate: new Date('02/22/86'), minDate: '02/20/86'},
      element: '<input type="text" ng-model="selectedDate" data-min-date="{{minDate}}" bs-datepicker>'
    },
    'options-minDate-today': {
      scope: {},
      element: '<input type="text" ng-model="selectedDate" data-min-date="today" bs-datepicker>'
    },
    'options-maxDate-today': {
      scope: {},
      element: '<input type="text" ng-model="selectedDate" data-max-date="today" bs-datepicker>'
    },
    'options-maxDate': {
      scope: {selectedDate: new Date('02/22/86'), maxDate: '02/24/86'},
      element: '<input type="text" ng-model="selectedDate" data-max-date="{{maxDate}}" bs-datepicker>'
    },
    'options-autoclose': {
      element: '<input type="text" ng-model="selectedDate" data-autoclose="1" bs-datepicker>'
    },
    'options-useNative': {
      element: '<input type="text" ng-model="selectedDate" data-use-native="1" bs-datepicker>'
    }
  };

  function compileDirective(template, locals) {
    template = templates[template];
    angular.extend(scope, template.scope || templates['default'].scope, locals);
    var element = $(template.element).appendTo(sandboxEl);
    element = $compile(element)(scope);
    scope.$digest();
    return jQuery(element[0]);
  }

  // Tests

  describe('with default template', function() {

    it('should open on focus', function() {
      var elm = compileDirective('default');
      expect(sandboxEl.children('.dropdown-menu.datepicker').length).toBe(0);
      angular.element(elm[0]).triggerHandler('focus');
      expect(sandboxEl.children('.dropdown-menu.datepicker').length).toBe(1);
    });

    it('should close on blur', function() {
      var elm = compileDirective('default');
      expect(sandboxEl.children('.dropdown-menu.datepicker').length).toBe(0);
      angular.element(elm[0]).triggerHandler('focus');
      angular.element(elm[0]).triggerHandler('blur');
      expect(sandboxEl.children('.dropdown-menu.datepicker').length).toBe(0);
    });

    it('should correctly compile inner content', function() {
      var elm = compileDirective('default');
      angular.element(elm[0]).triggerHandler('focus');
      expect(sandboxEl.find('.dropdown-menu tbody td').length).toBe(5);
      expect(sandboxEl.find('.dropdown-menu tbody .btn').length).toBe(7 * 5);
      expect(sandboxEl.find('.dropdown-menu thead button:eq(1)').text()).toBe(dateFilter(today, 'MMMM yyyy'));
    });

    it('should correctly display active date', function() {
      var elm = compileDirective('default');
      angular.element(elm[0]).triggerHandler('focus');
      expect(sandboxEl.find('.dropdown-menu tbody td .btn-primary').text().trim() * 1).toBe(today.getUTCDate());
    });

    it('should correctly navigate to upper month view', function() {
      var elm = compileDirective('default');
      var date = today.getDate(), month = today.getMonth();
      angular.element(elm[0]).triggerHandler('focus');
      expect(scope.$$childHead.$mode).toBe(0);
      angular.element(sandboxEl.find('.dropdown-menu thead button:eq(1)')[0]).triggerHandler('click');
      expect(scope.$$childHead.$mode).toBe(1);
    });

    describe('once in month view', function() {

      it('should correctly compile inner content', function() {
        var elm = compileDirective('default');
        angular.element(elm[0]).triggerHandler('focus');
        angular.element(sandboxEl.find('.dropdown-menu thead button:eq(1)')[0]).triggerHandler('click');
        expect(sandboxEl.find('.dropdown-menu tbody td').length).toBe(3);
        expect(sandboxEl.find('.dropdown-menu tbody .btn').length).toBe(4 * 3);
        expect(sandboxEl.find('.dropdown-menu thead button:eq(1)').text()).toBe(dateFilter(today, 'yyyy'));
      });

      it('should correctly display active date', function() {
        var elm = compileDirective('default');
        angular.element(elm[0]).triggerHandler('focus');
        angular.element(sandboxEl.find('.dropdown-menu thead button:eq(1)')[0]).triggerHandler('click');
        expect(sandboxEl.find('.dropdown-menu tbody td .btn-primary').text()).toBe(dateFilter(today, 'MMM'));
      });

    });

    it('should correctly support undefined values', function() {
      var elm = compileDirective('value-undefined');
      angular.element(elm[0]).triggerHandler('focus');
      expect(sandboxEl.find('.dropdown-menu tbody td').length).toBe(5);
      expect(sandboxEl.find('.dropdown-menu tbody .btn').length).toBe(7 * 5);
    });

    // iit('should only build the datepicker once', function() {
    //   var elm = compileDirective('value-past');
    //   angular.element(elm[0]).triggerHandler('focus');
    // });

    it('should support ngRepeat markup', function() {
      var elm = compileDirective('markup-ngRepeat');
      angular.element(elm.find('[bs-datepicker]:eq(0)')).triggerHandler('focus');
      expect(sandboxEl.find('.dropdown-menu tbody td').length).toBe(5);
      expect(sandboxEl.find('.dropdown-menu tbody .btn').length).toBe(7 * 5);
    });

  });

  // describe('using service', function() {

  //   it('should correctly open on next digest', function() {
  //     var myModal = $modal(templates['default'].scope.modal);
  //     scope.$digest();
  //     expect(bodyEl.children('.modal').length).toBe(1);
  //     myModal.hide();
  //     expect(bodyEl.children('.modal').length).toBe(0);
  //   });

  //   it('should correctly be destroyed', function() {
  //     var myModal = $modal(angular.extend(templates['default'].scope.modal));
  //     scope.$digest();
  //     expect(bodyEl.children('.modal').length).toBe(1);
  //     myModal.destroy();
  //     expect(bodyEl.children('.modal').length).toBe(0);
  //     expect(bodyEl.children().length).toBe(1);
  //   });

  //   it('should correctly work with ngClick', function() {
  //     var elm = compileDirective('markup-ngClick-service');
  //     var myModal = $modal(angular.extend({show: false}, templates['default'].scope.modal));
  //     scope.showModal = function() {
  //       myModal.$promise.then(myModal.show);
  //     };
  //     expect(bodyEl.children('.modal').length).toBe(0);
  //     angular.element(elm[0]).triggerHandler('click');
  //     expect(bodyEl.children('.modal').length).toBe(1);
  //   });

  // });

  describe('options', function() {

    describe('animation', function() {

      it('should default to `animation-fade` animation', function() {
        var elm = compileDirective('default');
        angular.element(elm[0]).triggerHandler('focus');
        expect(sandboxEl.children('.dropdown-menu').hasClass('animation-fade')).toBeTruthy();
      });

      it('should support custom animation', function() {
        var elm = compileDirective('options-animation');
        angular.element(elm[0]).triggerHandler('focus');
        expect(sandboxEl.children('.dropdown-menu').hasClass('animation-flipX')).toBeTruthy();
      });

    });

    describe('autoclose', function() {

      it('should close on select', function() {
        var elm = compileDirective('options-autoclose');
        expect(sandboxEl.children('.dropdown-menu.datepicker').length).toBe(0);
        angular.element(elm[0]).triggerHandler('focus');
        angular.element(sandboxEl.find('.dropdown-menu tbody .btn:first')).triggerHandler('click');
        expect(sandboxEl.children('.dropdown-menu.datepicker').length).toBe(0);
      });

    });

    describe('placement', function() {

      it('should default to `bottom-left` placement', function() {
        var elm = compileDirective('default');
        angular.element(elm[0]).triggerHandler('focus');
        expect(sandboxEl.children('.dropdown-menu').hasClass('bottom-left')).toBeTruthy();
      });

      it('should support placement', function() {
        var elm = compileDirective('options-placement');
        angular.element(elm[0]).triggerHandler('focus');
        expect(sandboxEl.children('.dropdown-menu').hasClass('bottom')).toBeTruthy();
      });

      it('should support exotic-placement', function() {
        var elm = compileDirective('options-placement-exotic');
        angular.element(elm[0]).triggerHandler('focus');
        expect(sandboxEl.children('.dropdown-menu').hasClass('bottom-right')).toBeTruthy();
      });

    });

    describe('trigger', function() {

      it('should support an alternative trigger', function() {
        var elm = compileDirective('options-trigger');
        expect(sandboxEl.children('.dropdown-menu').length).toBe(0);
        angular.element(elm[0]).triggerHandler('mouseenter');
        expect(sandboxEl.children('.dropdown-menu').length).toBe(1);
        angular.element(elm[0]).triggerHandler('mouseleave');
        expect(sandboxEl.children('.dropdown-menu').length).toBe(0);
      });

    });

    describe('template', function() {

      it('should support custom template', function() {
        $templateCache.put('custom', '

foo: {{selectedDate}}

');
        var elm = compileDirective('options-template');
        angular.element(elm[0]).triggerHandler('focus');
        expect(sandboxEl.find('.dropdown-inner').text()).toBe('foo: "' + scope.selectedDate.toISOString() + '"');
      });

      it('should support template with ngRepeat', function() {
        $templateCache.put('custom', '

{{i}}

');
        var elm = compileDirective('options-template');
        angular.element(elm[0]).triggerHandler('focus');
        expect(sandboxEl.find('.dropdown-inner').text()).toBe('123');
        // Consecutive toggles
        angular.element(elm[0]).triggerHandler('focus');
        angular.element(elm[0]).triggerHandler('blur');
        expect(sandboxEl.find('.dropdown-inner').text()).toBe('123');
      });

      it('should support template with ngClick', function() {
        $templateCache.put('custom', '

click me

');
        var elm = compileDirective('options-template');
        scope.dropdown = {counter: 0};
        angular.element(elm[0]).triggerHandler('focus');
        expect(angular.element(sandboxEl.find('.dropdown-inner > .btn')[0]).triggerHandler('click'));
        expect(scope.dropdown.counter).toBe(1);
        // Consecutive toggles
        angular.element(elm[0]).triggerHandler('focus');
        angular.element(elm[0]).triggerHandler('blur');
        expect(angular.element(sandboxEl.find('.dropdown-inner > .btn')[0]).triggerHandler('click'));
        expect(scope.dropdown.counter).toBe(2);
      });

    });

    describe('dateFormat', function() {

      it('should support a custom dateFormat', function() {
        var elm = compileDirective('options-dateFormat');
        expect(elm.val()).toBe('1986-02-22');
        angular.element(elm[0]).triggerHandler('focus');
        angular.element(sandboxEl.find('.dropdown-menu tbody .btn:first')).triggerHandler('click');
        expect(elm.val()).toBe('1986-01-25');
      });

    });

    describe('minDate', function() {

      it('should support a dynamic minDate', function() {
        var elm = compileDirective('options-minDate');
        angular.element(elm[0]).triggerHandler('focus');
        expect(sandboxEl.find('.dropdown-menu tbody button[disabled]').length).toBe(26);
        expect(sandboxEl.find('.dropdown-menu tbody button:contains(19)').is(':disabled')).toBeTruthy();
        expect(sandboxEl.find('.dropdown-menu tbody button:contains(20)').is(':disabled')).toBeFalsy();
        scope.minDate = '02/12/86';
        scope.$digest();
        expect(sandboxEl.find('.dropdown-menu tbody button[disabled]').length).toBe(18);
      });

      it('should support today as minDate', function() {
        var elm = compileDirective('options-minDate-today');
        angular.element(elm[0]).triggerHandler('focus');
        var todayDate = today.getDate();
        expect(sandboxEl.find('.dropdown-menu tbody button[disabled]').length > 0).toBeTruthy();
        expect(sandboxEl.find('.dropdown-menu tbody button:contains(' + todayDate + ')').is(':disabled')).toBeFalsy();
      });

    });

    describe('maxDate', function() {

      it('should support a dynamic maxDate', function() {
        var elm = compileDirective('options-maxDate');
        angular.element(elm[0]).triggerHandler('focus');
        expect(sandboxEl.find('.dropdown-menu tbody button[disabled]').length).toBe(4);
        expect(sandboxEl.find('.dropdown-menu tbody button:contains(24)').is(':disabled')).toBeFalsy();
        expect(sandboxEl.find('.dropdown-menu tbody button:contains(25)').is(':disabled')).toBeTruthy();
        scope.maxDate = '02/12/86';
        scope.$digest();
        expect(sandboxEl.find('.dropdown-menu tbody button[disabled]').length).toBe(16);
      });

      it('should support today as maxDate', function() {
        var elm = compileDirective('options-maxDate-today');
        angular.element(elm[0]).triggerHandler('focus');
        var date = new Date(), today = date.getDate();
        expect(sandboxEl.find('.dropdown-menu tbody button[disabled]').length > 0).toBeTruthy();
        expect(sandboxEl.find('.dropdown-menu tbody button:contains(' + today + ')').is(':disabled')).toBeFalsy();
      });

    });

    describe('useNative', function() {

      it('should correctly compile template according to useNative', function() {
        var elm = compileDirective('options-useNative');
        angular.element(elm[0]).triggerHandler('focus');
        // @TODO ?
      });

    });

  });

});
<|endoftext|>


<commit_before>// @flow strict

import { expect } from 'chai';
import { describe, it } from 'mocha';

import dedent from '../../jsutils/dedent';
import invariant from '../../jsutils/invariant';
import { GraphQLError } from '../GraphQLError';
import { printError } from '../printError';
import { Kind, parse, Source } from '../../language';

describe('printError', () => {
  it('prints an line numbers with correct padding', () => {
    const singleDigit = new GraphQLError(
      'Single digit line number with no padding',
      null,
      new Source('*', 'Test', { line: 9, column: 1 }),
      [0],
    );
    expect(printError(singleDigit)).to.equal(dedent`
      Single digit line number with no padding

      Test:9:1
      9: *
         ^
    `);

    const doubleDigit = new GraphQLError(
      'Left padded first line number',
      null,
      new Source('*\n', 'Test', { line: 9, column: 1 }),
      [0],
    );
    expect(printError(doubleDigit)).to.equal(dedent`
      Left padded first line number

      Test:9:1
       9: *
          ^
      10:
    `);
  });

  it('prints an error with nodes from different sources', () => {
    const docA = parse(
      new Source(
        dedent`
          type Foo {
            field: String
          }
        `,
        'SourceA',
      ),
    );
    const opA = docA.definitions[0];
    invariant(opA && opA.kind === Kind.OBJECT_TYPE_DEFINITION && opA.fields);
    const fieldA = opA.fields[0];

    const docB = parse(
      new Source(
        dedent`
          type Foo {
            field: Int
          }
        `,
        'SourceB',
      ),
    );
    const opB = docB.definitions[0];
    invariant(opB && opB.kind === Kind.OBJECT_TYPE_DEFINITION && opB.fields);
    const fieldB = opB.fields[0];

    const error = new GraphQLError('Example error with two nodes', [
      fieldA.type,
      fieldB.type,
    ]);

    expect(printError(error)).to.equal(dedent`
      Example error with two nodes

      SourceA:2:10
      1: type Foo {
      2:   field: String
                  ^
      3: }

      SourceB:2:10
      1: type Foo {
      2:   field: Int
                  ^
      3: }
    `);
  });
});
<commit_msg>printError: improve coverage (#1982)<commit_after>// @flow strict

import { expect } from 'chai';
import { describe, it } from 'mocha';

import dedent from '../../jsutils/dedent';
import invariant from '../../jsutils/invariant';
import { GraphQLError } from '../GraphQLError';
import { printError } from '../printError';
import { Kind, parse, Source } from '../../language';

describe('printError', () => {
  it('prints an error without location', () => {
    const error = new GraphQLError('Error without location');
    expect(printError(error)).to.equal('Error without location');
  });

  it('prints an error using node without location', () => {
    const error = new GraphQLError(
      'Error attached to node without location',
      parse('{ foo }', { noLocation: true }),
    );
    expect(printError(error)).to.equal(
      'Error attached to node without location',
    );
  });

  it('prints an line numbers with correct padding', () => {
    const singleDigit = new GraphQLError(
      'Single digit line number with no padding',
      null,
      new Source('*', 'Test', { line: 9, column: 1 }),
      [0],
    );
    expect(printError(singleDigit)).to.equal(dedent`
      Single digit line number with no padding

      Test:9:1
      9: *
         ^
    `);

    const doubleDigit = new GraphQLError(
      'Left padded first line number',
      null,
      new Source('*\n', 'Test', { line: 9, column: 1 }),
      [0],
    );
    expect(printError(doubleDigit)).to.equal(dedent`
      Left padded first line number

      Test:9:1
       9: *
          ^
      10:
    `);
  });

  it('prints an error with nodes from different sources', () => {
    const docA = parse(
      new Source(
        dedent`
          type Foo {
            field: String
          }
        `,
        'SourceA',
      ),
    );
    const opA = docA.definitions[0];
    invariant(opA && opA.kind === Kind.OBJECT_TYPE_DEFINITION && opA.fields);
    const fieldA = opA.fields[0];

    const docB = parse(
      new Source(
        dedent`
          type Foo {
            field: Int
          }
        `,
        'SourceB',
      ),
    );
    const opB = docB.definitions[0];
    invariant(opB && opB.kind === Kind.OBJECT_TYPE_DEFINITION && opB.fields);
    const fieldB = opB.fields[0];

    const error = new GraphQLError('Example error with two nodes', [
      fieldA.type,
      fieldB.type,
    ]);

    expect(printError(error)).to.equal(dedent`
      Example error with two nodes

      SourceA:2:10
      1: type Foo {
      2:   field: String
                  ^
      3: }

      SourceB:2:10
      1: type Foo {
      2:   field: Int
                  ^
      3: }
    `);
  });
});
<|endoftext|>


<commit_before>"use strict";

const path = require('path');
const fs   = require('fs');
const _memoize = require('lodash/memoize');

/* a note on the un-cached fs.readFileSync:
 *   I've done some tests with caching the function (with _.memoize), however
 *   _with_ and _without_ cache didn't make a difference, performance-wise.
 *   This is probably because the OS already does some clever caching of its own.
 *
 *   Test config:
 *   - OSX 10.11.6
 *   - MacBook Pro (15-inch, 2.53GHz, Mid 2009)
 *   - Memory: 4 Gb 1067 MHz DDR3
 *   - Disk: 256Gb SATA with a HFS+ journaled file system (a spinning disk to be sure)
 *   - ~400 javascript, typescript and coffeescript modules
 */

const readPackageDeps =
    (pFileDir) => {
        let lRetval = null;
        let lPackageContent = null;
        let lLookupDir = path.resolve(pFileDir);

        do {
            try {
                // find the closest package.json from pFileDir
                lPackageContent = fs.readFileSync(path.join(lLookupDir, 'package.json'), 'utf8');
            } catch (e) {
                const lNextDir = path.dirname(lLookupDir);

                if (lNextDir === lLookupDir) {
                    // reached root directory
                    break;
                }
                lLookupDir = lNextDir;
            }
        } while (lPackageContent === null);

        if (lPackageContent !== null) {
            try {
                lRetval = JSON.parse(lPackageContent);
            } catch (e) {
                // left empty on purpose
            }
        }
        return lRetval;
    };

module.exports = _memoize(readPackageDeps);
<commit_msg>refactor(readPackageDeps): incorporate @ajafff's recursive revision<commit_after>"use strict";

const path = require('path');
const fs   = require('fs');
const _memoize = require('lodash/memoize');

const readPackageDeps = _memoize((pFileDir) => {
    let lRetval = null;

    try {
        // find the closest package.json from pFileDir
        const lPackageContent = fs.readFileSync(path.join(pFileDir, 'package.json'), 'utf8');

        try {
            lRetval = JSON.parse(lPackageContent);
        } catch (e) {
            // left empty on purpose
        }
    } catch (e) {
        const lNextDir = path.dirname(pFileDir);

        if (lNextDir !== pFileDir) {
            // not yet reached root directory
            lRetval = readPackageDeps(lNextDir);
        }
    }
    return lRetval;
});

/**
 * return the contents of the package.json closest to the passed
 * folder (or null if there's no such package.json/ that package.json
 * is invalid).
 *
 * This behavior is consistent with node's lookup mechanism
 *
 * @param {string} pFileDir the folder relative to which to find
 *                          the package.json
 * @return {any} the contents of the package.json as a javascript
 *               object or null if the package.json could not be
 *               found or is invalid
 */
module.exports = readPackageDeps;
<|endoftext|>


<commit_before>/* eslint-env jest */

const manifest = require('../manifest.json')
const manifestAddonStore = require('../manifest.addon-store-overrides.json')
const manifestSelfHosted = require('../manifest.self-hosted-overrides.json')

// Tests to avoid accidentally requesting new permissions.

test('manifest permissions have not changed', () => {
  var permissions = manifest['permissions']
  expect(permissions).toEqual([])
})

test('content script permissions have not changed', () => {
  expect(manifest['content_scripts']).toBeUndefined()
})

test('manifest does not extend devtools', () => {
  expect(manifest['devtools_page']).toBeUndefined()
})

test('manifest does not use plugins', () => {
  expect(manifest['plugins']).toBeUndefined()
})

test('addon store manifest overrides are as expected', () => {
  expect(manifestAddonStore).toEqual({
    'applications': {
      'gecko': {
        'id': 'tabforacause@tabforacause.org',
        'strict_min_version': '56.0a1'
      }
    }
  })
})

test('self-hosted manifest overrides are as expected', () => {
  expect(manifestSelfHosted).toEqual({
    'applications': {
      'gecko': {
        'id': 'contact@tabforacause.org',
        'strict_min_version': '56.0a1',
        'update_url': 'https://tab.gladly.io/firefox/update.json'
      }
    }
  })
})

// Tests action property exists
// Action key must exist in manifest to use chrome.action API
// https://developer.chrome.com/docs/extensions/reference/action/#manifest

test('browserAction property exists', () => {
  expect(manifest['browser_action']).toBeTruthy()
})

// Basic display tests.

test('extension name is correct', () => {
  expect(manifest['name']).toBe('Tab for a Cause')
})
<commit_msg>Clarify browser_action jest test reason<commit_after>/* eslint-env jest */

const manifest = require('../manifest.json')
const manifestAddonStore = require('../manifest.addon-store-overrides.json')
const manifestSelfHosted = require('../manifest.self-hosted-overrides.json')

// Tests to avoid accidentally requesting new permissions.

test('manifest permissions have not changed', () => {
  var permissions = manifest['permissions']
  expect(permissions).toEqual([])
})

test('content script permissions have not changed', () => {
  expect(manifest['content_scripts']).toBeUndefined()
})

test('manifest does not extend devtools', () => {
  expect(manifest['devtools_page']).toBeUndefined()
})

test('manifest does not use plugins', () => {
  expect(manifest['plugins']).toBeUndefined()
})

test('addon store manifest overrides are as expected', () => {
  expect(manifestAddonStore).toEqual({
    'applications': {
      'gecko': {
        'id': 'tabforacause@tabforacause.org',
        'strict_min_version': '56.0a1'
      }
    }
  })
})

test('self-hosted manifest overrides are as expected', () => {
  expect(manifestSelfHosted).toEqual({
    'applications': {
      'gecko': {
        'id': 'contact@tabforacause.org',
        'strict_min_version': '56.0a1',
        'update_url': 'https://tab.gladly.io/firefox/update.json'
      }
    }
  })
})

// Tests action property exists
// browser_action property must exist for
// Firefox to show extension icon in toolbar

test('browser_action property exists', () => {
  expect(manifest['browser_action']).toBeTruthy()
})

// Basic display tests.

test('extension name is correct', () => {
  expect(manifest['name']).toBe('Tab for a Cause')
})
<|endoftext|>