<commit_before>/* @flow */
/* eslint no-use-before-define: "off" */
import {
  GraphQLBoolean,
  GraphQLEnumType,
  GraphQLInt,
  GraphQLList,
  GraphQLNonNull,
  GraphQLObjectType,
  GraphQLString,
} from 'graphql';
import Sequelize from 'sequelize';
import { attributeFields } from 'graphql-sequelize';
import JSONType from 'graphql-sequelize/lib/types/jsonType';
import searchQuery from 'search-query-parser';
import { Search } from './models';
import patchEscapeLiteral from './sequelizePatchLiteral';

patchEscapeLiteral();

export const searchOptions = {
  keywords: [
    'is',
    'board',
    'party',
    'state',
    'disposition',
    'awarded',
    'type',
  ],
  groups: {
    is: [
      ['case', 'party'],
    ],
    type: [
      ['employment', 'consumer'],
    ],
  },
  ranges: [
    'filed',
    'closed',
  ],
};

const NULL_VALUES = [
  '---',
  'unknown',
  'null',
];
const isNullValue = (v: string): bool => NULL_VALUES.indexOf(
  v.toLowerCase()) !== -1;

const PREVAILING_PARTIES = [
  ...NULL_VALUES,
  'business',
  'consumer/business',
  'consumer',
  'employee',
  'employee/business',
  'home owner/business',
  'home owner',
];

const DISPOSITIONS = [
  ...NULL_VALUES,
  'settled',
  'administrative',
  'dismissed',
  'awarded',
  'withdrawn',
  'impasse',
];

const validateKeyword = (word: KeywordType, match: RegExp | Array<string>) => {
  if (Array.isArray(match)) {
    const arr: Array<string> = match;

    return safeMap(word, w => arr.indexOf(w.toLowerCase()) !== -1)
      .reduce((a, b) => a && b, true);
  }

  const re: RegExp = match;

  return safeMap(word, w => re.test(w)).reduce((a, b) => a && b, true);
};

const validateRange = ({ from, to }: RangeType, re: RegExp): bool => {
  if (to != null && !re.test(to)) {
    return false;
  }
  return re.test(from);
};

const safeMap = (val: any | Array<any>, func): Array<any> => (
  typeof val === 'string'
    ? [val].map(func)
    : val.map(func)
);
const remapIs = {
  lawfirm: 'law firm',
  nonconsumer: 'non consumer',
};
const isTypes = {
  case: 'type = ?',
  party: 'type = ?',

  'law firm': 'document->>\'type\' ILIKE ?',
  'non consumer': 'document->>\'type\' ILIKE ?',
  attorney: 'document->>\'type\' ILIKE ?',
  arbitrator: 'document->>\'type\' ILIKE ?',
  consumer: 'document->>\'type\' ILIKE ?',
};
const convertIs = (vals: string | Array<string>) => {
  const filters = [];
  const is = safeMap(vals,
    val => val.toLowerCase().trim().replace(/[-_]/g, ' ').replace(/\s+/g, ' '))
    .map(val => (val in remapIs ? remapIs[val] : val))
    .reduce((groups, val) => {
      if (val in isTypes) {
        if (!groups[isTypes[val]]) {
          groups[isTypes[val]] = []; // eslint-disable-line no-param-reassign
        }
        groups[isTypes[val]].push(val);
      }

      return groups;
    }, {});

  for (const key of Object.keys(is)) {
    filters.push({ $or: is[key].map(v => [key, v]) });
  }

  return filters;
};
const filterRange = (field, { from, to }) => {
  if (from != null && to != null) {
    return [`${field} BETWEEN ? AND ?`, from, to];
  }

  if (from != null) {
    if ((/^[<>]/.test(from))) {
      const op = from.substr(0, 1);
      const val = from.substr(1);

      return [`${field} ${op}= ?`, val];
    }

    return [`${field} = ?`, from];
  }

  return [];
};

function buildQuery(parsed: ParsedType): [any, Array<any>] {
  let where = {};
  const order = [];

  if (typeof parsed === 'string' || typeof parsed.text === 'string') {
    let term: string = '';
    if (typeof parsed === 'string') {
      term = parsed;
    }
    if (typeof parsed.text === 'string') {
      term = parsed.text;
    }

    order.push(Sequelize.literal(
      `ts_rank(vector, plainto_tsquery('simple', '${term.replace('\'',
        '\'\'')}')) DESC`));
    where = {
      $and: [
        where,
        {
          $or: [
            ['vector @@ plainto_tsquery(\'simple\', ?)', term],
            {
              $and: term.split(/\s+/g)
                .map(word => ['index ILIKE ?', [`%${word}%`]]),
            },
          ],
        },
      ],
    };
  }

  if (typeof parsed !== 'string') {
    if (parsed.type != null) {
      where = {
        $and: [
          where, {
            $or: safeMap(parsed.type,
              s => ['document->>\'normal_type\' ILIKE ?', s]),
          }],
      };
    }
    if (parsed.state != null) {
      where = {
        $and: [
          where,
          {
            $or: safeMap(parsed.state,
              s => ['document->>\'consumer_rep_state\' ILIKE ?', s]),
          }],
      };
    }
    if (parsed.board != null &&
      validateKeyword(parsed.board, ['aaa', 'jams'])) {
      where = {
        $and: [
          where,
          {
            $or: safeMap(parsed.board,
              b => ['document->>\'arbitration_board\' ILIKE ?', b]),
          }],
      };
    }
    if (parsed.disposition != null &&
      validateKeyword(parsed.disposition, DISPOSITIONS)) {
      where = {
        $and: [
          where,
          {
            $or: safeMap(parsed.disposition,
              d => ['document->>\'type_of_disposition\' ILIKE ?', d]),
          }],
      };
    }
    if (parsed.awarded != null &&
      validateKeyword(parsed.awarded, PREVAILING_PARTIES)) {
      where = {
        $and: [
          where, {
            $or: safeMap(parsed.awarded, a => (
                isNullValue(a)
                  ? ['document->>\'prevailing_party\' LIKE \'---\'']
                  : ['document->>\'prevailing_party\' ILIKE ?', a]
              ),
            ),
          }],
      };
    }
    if (parsed.party != null) {
      where = {
        $and: [
          where,
          ...safeMap(parsed.party,
            p => ['(document->\'names\') ? ?', Sequelize.literal('?'), p])],
      };
    }
    if (parsed.filed != null &&
      validateRange(parsed.filed, /^[<>]?\d+\/\d+\/\d+$/)) {
      where = {
        $and: [
          where,
          filterRange('(document->>\'filing_date\')::DATE', parsed.filed)],
      };
    }
    if (parsed.closed != null &&
      validateRange(parsed.closed, /^[<>]?\d+\/\d+\/\d+$/)) {
      where = {
        $and: [
          where,
          filterRange('(document->>\'close_date\')::DATE', parsed.closed)],
      };
    }
    if (parsed.is != null) {
      where = { $and: [where, convertIs(parsed.is)] };
    }
  }

  console.log(JSON.stringify(where, null, 2));
  return [where, order];
}

export const SearchType = new GraphQLObjectType({
  name: 'Search',
  fields: () => ({
    query: { type: GraphQLString },
    Results: {
      type: ResultsType,
      args: {
        page: { type: GraphQLInt, defaultValue: 1 },
        perPage: { type: GraphQLInt, defaultValue: 20 },
        sortBy: { type: GraphQLString },
        sortDir: {
          type: new GraphQLEnumType({
            name: 'SortDir',
            values: {
              ASC: { value: 'ASC' },
              DESC: { value: 'DESC' },
            },
          }),
          defaultValue: 'ASC',
        },
      },
      async resolve({ query }, { sortBy, sortDir, page, perPage }) {
        const parsed: ParsedType = searchQuery.parse(query, searchOptions);
        const [where, order] = buildQuery(parsed);

        // Fallback sorting to make sure we get some semblance of deterministic sort
        order.push(['id', 'asc']);

        // If user specific a sort, order by that first
        if (sortBy) {
          order.unshift([
            Sequelize.literal(`document->'${sortBy.replace('\'', '\'\'')}'`),
            sortDir,
          ]);
        }

        const results = await Search.findAndCount({
          where,
          order,
          limit: perPage,
          offset: (page - 1) * perPage,
        });

        return {
          query,
          page,
          pages: Math.ceil(results.count / perPage),
          perPage,
          where,
          order,
          total: results.count,
          edges: results.rows.map(result => ({
            cursor: `${result.type}:${result.id}`,
            node: result,
          })),
        };
      },
    },
  }),
});

export const ResultsType = new GraphQLObjectType({
  name: 'Results',
  fields: () => ({
    total: { type: GraphQLInt },
    page: { type: GraphQLInt },
    pages: { type: GraphQLInt },
    perPage: { type: GraphQLInt },
    hasPrevPage: {
      type: GraphQLBoolean,
      resolve({ page }) {
        return page > 1;
      },
    },
    hasNextPage: {
      type: GraphQLBoolean,
      resolve({ total, page, perPage }) {
        return (page * perPage) < total;
      },
    },
    edges: {
      type: new GraphQLList(new GraphQLObjectType({
        name: 'ResultEdge',
        fields: () => ({
          cursor: { type: GraphQLString },
          node: {
            type: new GraphQLObjectType({
              name: 'ResultNode',
              fields: () => ({
                ...attributeFields(Search),
                document: { type: JSONType },
              }),
            }),
          },
        }),
      })),
    },
  }),
});

type KeywordType = string | Array<string>;
type RangeType = {
  from: string,
  to: ?string
};
type ParsedType = string | {
  text?: string,
  is?: KeywordType,
  board?: KeywordType,
  disposition?: KeywordType,
  awarded?: KeywordType,
  closed?: RangeType,
  filed?: RangeType,
  party?: string,
  exclude?: ParsedType,
};

export default {
  fields: {
    Search: {
      type: SearchType,
      resolve(context: any, { query }: { query: string }) {
        return { query };
      },
      args: {
        query: { type: new GraphQLNonNull(GraphQLString) },
      },
    },
  },
};
<commit_msg>Implicit is:case with plaintext terms<commit_after>/* @flow */
/* eslint no-use-before-define: "off" */
import {
  GraphQLBoolean,
  GraphQLEnumType,
  GraphQLInt,
  GraphQLList,
  GraphQLNonNull,
  GraphQLObjectType,
  GraphQLString,
} from 'graphql';
import Sequelize from 'sequelize';
import { attributeFields } from 'graphql-sequelize';
import JSONType from 'graphql-sequelize/lib/types/jsonType';
import searchQuery from 'search-query-parser';
import { Search } from './models';
import patchEscapeLiteral from './sequelizePatchLiteral';

patchEscapeLiteral();

export const searchOptions = {
  keywords: [
    'is',
    'board',
    'party',
    'state',
    'disposition',
    'awarded',
    'type',
  ],
  groups: {
    is: [
      ['case', 'party'],
    ],
    type: [
      ['employment', 'consumer'],
    ],
  },
  ranges: [
    'filed',
    'closed',
  ],
};

const NULL_VALUES = [
  '---',
  'unknown',
  'null',
];
const isNullValue = (v: string): bool => NULL_VALUES.indexOf(
  v.toLowerCase()) !== -1;

const PREVAILING_PARTIES = [
  ...NULL_VALUES,
  'business',
  'consumer/business',
  'consumer',
  'employee',
  'employee/business',
  'home owner/business',
  'home owner',
];

const DISPOSITIONS = [
  ...NULL_VALUES,
  'settled',
  'administrative',
  'dismissed',
  'awarded',
  'withdrawn',
  'impasse',
];

const validateKeyword = (word: KeywordType, match: RegExp | Array<string>) => {
  if (Array.isArray(match)) {
    const arr: Array<string> = match;

    return safeMap(word, w => arr.indexOf(w.toLowerCase()) !== -1)
      .reduce((a, b) => a && b, true);
  }

  const re: RegExp = match;

  return safeMap(word, w => re.test(w)).reduce((a, b) => a && b, true);
};

const validateRange = ({ from, to }: RangeType, re: RegExp): bool => {
  if (to != null && !re.test(to)) {
    return false;
  }
  return re.test(from);
};

const safeMap = (val: any | Array<any>, func): Array<any> => (
  typeof val === 'string'
    ? [val].map(func)
    : val.map(func)
);
const remapIs = {
  lawfirm: 'law firm',
  nonconsumer: 'non consumer',
};
const isTypes = {
  case: 'type = ?',
  party: 'type = ?',

  'law firm': 'document->>\'type\' ILIKE ?',
  'non consumer': 'document->>\'type\' ILIKE ?',
  attorney: 'document->>\'type\' ILIKE ?',
  arbitrator: 'document->>\'type\' ILIKE ?',
  consumer: 'document->>\'type\' ILIKE ?',
};
const convertIs = (vals: string | Array<string>) => {
  const filters = [];
  const is = safeMap(vals,
    val => val.toLowerCase().trim().replace(/[-_]/g, ' ').replace(/\s+/g, ' '))
    .map(val => (val in remapIs ? remapIs[val] : val))
    .reduce((groups, val) => {
      if (val in isTypes) {
        if (!groups[isTypes[val]]) {
          groups[isTypes[val]] = []; // eslint-disable-line no-param-reassign
        }
        groups[isTypes[val]].push(val);
      }

      return groups;
    }, {});

  for (const key of Object.keys(is)) {
    filters.push({ $or: is[key].map(v => [key, v]) });
  }

  return filters;
};
const filterRange = (field, { from, to }) => {
  if (from != null && to != null) {
    return [`${field} BETWEEN ? AND ?`, from, to];
  }

  if (from != null) {
    if ((/^[<>]/.test(from))) {
      const op = from.substr(0, 1);
      const val = from.substr(1);

      return [`${field} ${op}= ?`, val];
    }

    return [`${field} = ?`, from];
  }

  return [];
};

function buildQuery(parsed: ParsedType): [any, Array<any>] {
  let where = convertIs(
    (typeof parsed === 'object' && parsed.is != null)
      ? parsed.is
      : 'case'
  );
  const order = [];

  if (typeof parsed === 'string' || typeof parsed.text === 'string') {
    let term: string = '';
    if (typeof parsed === 'string') {
      term = parsed;
    }
    if (typeof parsed.text === 'string') {
      term = parsed.text;
    }

    order.push(Sequelize.literal(
      `ts_rank(vector, plainto_tsquery('simple', '${term.replace('\'',
        '\'\'')}')) DESC`));
    where = {
      $and: [
        where,
        {
          $or: [
            ['vector @@ plainto_tsquery(\'simple\', ?)', term],
            {
              $and: term.split(/\s+/g)
                .map(word => ['index ILIKE ?', [`%${word}%`]]),
            },
          ],
        },
      ],
    };
  }

  if (typeof parsed !== 'string') {
    if (parsed.type != null) {
      where = {
        $and: [
          where, {
            $or: safeMap(parsed.type,
              s => ['document->>\'normal_type\' ILIKE ?', s]),
          }],
      };
    }
    if (parsed.state != null) {
      where = {
        $and: [
          where,
          {
            $or: safeMap(parsed.state,
              s => ['document->>\'consumer_rep_state\' ILIKE ?', s]),
          }],
      };
    }
    if (parsed.board != null &&
      validateKeyword(parsed.board, ['aaa', 'jams'])) {
      where = {
        $and: [
          where,
          {
            $or: safeMap(parsed.board,
              b => ['document->>\'arbitration_board\' ILIKE ?', b]),
          }],
      };
    }
    if (parsed.disposition != null &&
      validateKeyword(parsed.disposition, DISPOSITIONS)) {
      where = {
        $and: [
          where,
          {
            $or: safeMap(parsed.disposition,
              d => ['document->>\'type_of_disposition\' ILIKE ?', d]),
          }],
      };
    }
    if (parsed.awarded != null &&
      validateKeyword(parsed.awarded, PREVAILING_PARTIES)) {
      where = {
        $and: [
          where, {
            $or: safeMap(parsed.awarded, a => (
                isNullValue(a)
                  ? ['document->>\'prevailing_party\' LIKE \'---\'']
                  : ['document->>\'prevailing_party\' ILIKE ?', a]
              ),
            ),
          }],
      };
    }
    if (parsed.party != null) {
      where = {
        $and: [
          where,
          ...safeMap(parsed.party,
            p => ['(document->\'names\') ? ?', Sequelize.literal('?'), p])],
      };
    }
    if (parsed.filed != null &&
      validateRange(parsed.filed, /^[<>]?\d+\/\d+\/\d+$/)) {
      where = {
        $and: [
          where,
          filterRange('(document->>\'filing_date\')::DATE', parsed.filed)],
      };
    }
    if (parsed.closed != null &&
      validateRange(parsed.closed, /^[<>]?\d+\/\d+\/\d+$/)) {
      where = {
        $and: [
          where,
          filterRange('(document->>\'close_date\')::DATE', parsed.closed)],
      };
    }
  }

  return [where, order];
}

export const SearchType = new GraphQLObjectType({
  name: 'Search',
  fields: () => ({
    query: { type: GraphQLString },
    Results: {
      type: ResultsType,
      args: {
        page: { type: GraphQLInt, defaultValue: 1 },
        perPage: { type: GraphQLInt, defaultValue: 20 },
        sortBy: { type: GraphQLString },
        sortDir: {
          type: new GraphQLEnumType({
            name: 'SortDir',
            values: {
              ASC: { value: 'ASC' },
              DESC: { value: 'DESC' },
            },
          }),
          defaultValue: 'ASC',
        },
      },
      async resolve({ query }, { sortBy, sortDir, page, perPage }) {
        const parsed: ParsedType = searchQuery.parse(query, searchOptions);
        const [where, order] = buildQuery(parsed);

        // Fallback sorting to make sure we get some semblance of deterministic sort
        order.push(['id', 'asc']);

        // If user specific a sort, order by that first
        if (sortBy) {
          order.unshift([
            Sequelize.literal(`document->'${sortBy.replace('\'', '\'\'')}'`),
            sortDir,
          ]);
        }

        const results = await Search.findAndCount({
          where,
          order,
          limit: perPage,
          offset: (page - 1) * perPage,
        });

        return {
          query,
          page,
          pages: Math.ceil(results.count / perPage),
          perPage,
          where,
          order,
          total: results.count,
          edges: results.rows.map(result => ({
            cursor: `${result.type}:${result.id}`,
            node: result,
          })),
        };
      },
    },
  }),
});

export const ResultsType = new GraphQLObjectType({
  name: 'Results',
  fields: () => ({
    total: { type: GraphQLInt },
    page: { type: GraphQLInt },
    pages: { type: GraphQLInt },
    perPage: { type: GraphQLInt },
    hasPrevPage: {
      type: GraphQLBoolean,
      resolve({ page }) {
        return page > 1;
      },
    },
    hasNextPage: {
      type: GraphQLBoolean,
      resolve({ total, page, perPage }) {
        return (page * perPage) < total;
      },
    },
    edges: {
      type: new GraphQLList(new GraphQLObjectType({
        name: 'ResultEdge',
        fields: () => ({
          cursor: { type: GraphQLString },
          node: {
            type: new GraphQLObjectType({
              name: 'ResultNode',
              fields: () => ({
                ...attributeFields(Search),
                document: { type: JSONType },
              }),
            }),
          },
        }),
      })),
    },
  }),
});

type KeywordType = string | Array<string>;
type RangeType = {
  from: string,
  to: ?string
};
type ParsedType = string | {
  text?: string,
  is?: KeywordType,
  board?: KeywordType,
  disposition?: KeywordType,
  awarded?: KeywordType,
  closed?: RangeType,
  filed?: RangeType,
  party?: string,
  exclude?: ParsedType,
};

export default {
  fields: {
    Search: {
      type: SearchType,
      resolve(context: any, { query }: { query: string }) {
        return { query };
      },
      args: {
        query: { type: new GraphQLNonNull(GraphQLString) },
      },
    },
  },
};
<|endoftext|>


<commit_before>var async = require('async');
var debug = require('debug')('site-parser:parse');
var colors = require('colors');
var util = require('util');

exports = module.exports = function(template, callback) {
    if (!template) return debug('no template supplied');

    async.waterfall([
        this._fetch.bind(this, template, this.agent),
        this._handler.bind(this, template)
    ], callback);
};
<commit_msg>use correct early exit<commit_after>var async = require('async');

exports = module.exports = function(template, callback) {
    if (!template) return callback('no template supplied');

    async.waterfall([
        this._fetch.bind(this, template, this.agent),
        this._handler.bind(this, template)
    ], callback);
};
<|endoftext|>


<commit_before>/* eslint-disable import/no-extraneous-dependencies,react/jsx-filename-extension */
import Enzyme, { mount, shallow } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
import toJson from 'enzyme-to-json';
import React from 'react';
import VeasyForm from '../src/VeasyForm';
import * as lib from '../src/helpers';

Enzyme.configure({ adapter: new Adapter() });

const Input = () => <input type="text" />;
const Email = () => <input type="email" />;

describe('Test the <Veasy />', () => {
  let mockSchema;
  let mockTarget;
  let mockComponent;
  let componentToRender;

  beforeEach(() => {
    mockSchema = {
      title: {
        minLength: 1,
        default: ''
      }
    };
    mockTarget = {
      name: 'title',
      value: 'abc'
    };
    mockComponent = {
      state: {
        isFormOK: false,
        title: {
          status: 'normal',
          errorText: '',
          value: ''
        }
      },
      setState: x => {}
    };
    componentToRender = (
      <VeasyForm
        schema={mockSchema}
        allState={mockComponent.state}
        update={mockComponent.setState}
      >

            <Input name="title" />

        <input />

          <Email super="ok" cool="true" />

      </VeasyForm>
    );
  });

  test('should allows to set 3 props', () => {
    const wrapper = mount(
      <VeasyForm
        schema={mockSchema}
        allState={mockComponent.state}
        update={mockComponent.setState}
      >
        <Input name="title" />
      </VeasyForm>
    );
    expect(wrapper.find(VeasyForm)).toHaveLength(1);
    expect(wrapper.props().schema).toEqual(mockSchema);
    expect(wrapper.props().allState).toEqual(mockComponent.state);
    expect(wrapper.props().update).toEqual(mockComponent.setState);
  });

  test('Should render a section as container with a onChange function', () => {
    const wrapper = mount(
      <VeasyForm
        schema={mockSchema}
        allState={mockComponent.state}
        update={mockComponent.setState}
      >
        <Input name="title" />
        <input />
        <input />
      </VeasyForm>
    );
    expect(wrapper.find('form')).toHaveLength(1);
    expect(wrapper.find('section')).toHaveLength(0);
    expect(typeof wrapper.find('form').prop('onChange')).toEqual('function');
    wrapper.find('form').prop('onChange')({
      preventDefault: () => {},
      target: mockTarget
    });
  });

  test('Should render 3 inputs as children of section', () => {
    const wrapper = shallow(
      <VeasyForm
        schema={mockSchema}
        allState={mockComponent.state}
        update={mockComponent.setState}
      >
        <input />
        <input />
        <input />
      </VeasyForm>
    );
    const children = wrapper.find('form').children();
    expect(children).toHaveLength(3);
    expect(children.find('input')).toHaveLength(3);
  });

  test('The 1st children should have extra props', () => {
    const wrapper = shallow(
      <VeasyForm
        schema={mockSchema}
        allState={mockComponent.state}
        update={mockComponent.setState}
      >
        <Input name="title" />
        <input />
        <input />
      </VeasyForm>
    );
    const targetInput = wrapper.find('Input').at(0);
    expect(targetInput.prop('name')).toEqual('title');
    expect(targetInput.prop('status')).toEqual('normal');
    expect(targetInput.prop('errorText')).toEqual('');
    expect(targetInput.prop('value')).toEqual('');
  });

  test('The 2nd and 3rd children shouldn`t have extra props', () => {
    const wrapper = shallow(
      <VeasyForm
        schema={mockSchema}
        allState={mockComponent.state}
        update={mockComponent.setState}
      >
        <Input name="title" />
        <input />
        <input />
      </VeasyForm>
    );
    const targetInput = wrapper.find('input').at(1);
    expect(targetInput.prop('name')).toBe(undefined);
    expect(targetInput.prop('status')).toBe(undefined);
    expect(targetInput.prop('errorText')).toBe(undefined);
    expect(targetInput.prop('value')).toBe(undefined);

    const targetInput1 = wrapper.find('input').at(2);
    expect(targetInput1.prop('name')).toBe(undefined);
    expect(targetInput1.prop('status')).toBe(undefined);
    expect(targetInput1.prop('errorText')).toBe(undefined);
    expect(targetInput1.prop('value')).toBe(undefined);
  });

  test('Should bind recursive element', () => {
    const wrapper = shallow(componentToRender);
    const targetInput = wrapper.find('Input').at(0);
    expect(targetInput.prop('name')).toBe('title');
    expect(targetInput.prop('status')).toBe('normal');
    expect(targetInput.prop('errorText')).toBe('');
    expect(targetInput.prop('value')).toBe('');
  });

  test('Should maintain the user`s prop', () => {
    const wrapper = shallow(componentToRender);
    const targetInput = wrapper.find('Email').at(0);
    expect(targetInput.prop('super')).toBe('ok');
    expect(targetInput.prop('cool')).toBe('true');
  });

  test('trigger onBlur should invoke triggerValidation', () => {
    const wrapper = shallow(componentToRender);
    const mockTrigger = jest.fn();
    wrapper.instance().triggerValidation = mockTrigger;
    expect(mockTrigger.mock.calls.length).toBe(0);
    const target = wrapper.find('form').at(0);
    target.simulate('blur');
    expect(mockTrigger.mock.calls.length).toBe(1);
  });

  test('should trigger lib.resetForm', () => {
    const mockReset = jest.fn();
    const mockUpdate = jest.fn();
    lib.resetForm = mockReset;
    const wrapper = shallow(
      <VeasyForm
        schema={mockSchema}
        allState={mockComponent.state}
        update={mockUpdate}
      >

            <Input name="title" />

        <input />

          <Email super="ok" cool="true" />

      </VeasyForm>
    );
    const target = wrapper.find('form').at(0);
    target.simulate('reset', { preventDefault: () => {} });
    expect(mockReset.mock.calls.length).toBe(1);
    expect(mockReset).toBeCalledWith({
      title: {
        default: '',
        minLength: 1
      }
    });
    expect(mockUpdate.mock.calls.length).toBe(1);
  });

  test('Should render a form with 3 inputs with extra props', () => {
    const wrapper = shallow(
      <VeasyForm
        schema={mockSchema}
        allState={mockComponent.state}
        update={mockComponent.setState}
        name='super'
        action='google'
        onSubmit={() => {}}
      >
        <Input name="title" />
        <input />
        <input />
      </VeasyForm>
    );
    console.log(wrapper.debug());
    const form = wrapper.find('form').at(0);
    expect(form.prop('name')).toEqual('super');
    expect(form.prop('action')).toEqual('google');
    expect(typeof form.prop('onSubmit')).toEqual('function');
  });
});
<commit_msg>Remove debug from test case<commit_after>/* eslint-disable import/no-extraneous-dependencies,react/jsx-filename-extension */
import Enzyme, { mount, shallow } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
import toJson from 'enzyme-to-json';
import React from 'react';
import VeasyForm from '../src/VeasyForm';
import * as lib from '../src/helpers';

Enzyme.configure({ adapter: new Adapter() });

const Input = () => <input type="text" />;
const Email = () => <input type="email" />;

describe('Test the <Veasy />', () => {
  let mockSchema;
  let mockTarget;
  let mockComponent;
  let componentToRender;

  beforeEach(() => {
    mockSchema = {
      title: {
        minLength: 1,
        default: ''
      }
    };
    mockTarget = {
      name: 'title',
      value: 'abc'
    };
    mockComponent = {
      state: {
        isFormOK: false,
        title: {
          status: 'normal',
          errorText: '',
          value: ''
        }
      },
      setState: x => {}
    };
    componentToRender = (
      <VeasyForm
        schema={mockSchema}
        allState={mockComponent.state}
        update={mockComponent.setState}
      >

            <Input name="title" />

        <input />

          <Email super="ok" cool="true" />

      </VeasyForm>
    );
  });

  test('should allows to set 3 props', () => {
    const wrapper = mount(
      <VeasyForm
        schema={mockSchema}
        allState={mockComponent.state}
        update={mockComponent.setState}
      >
        <Input name="title" />
      </VeasyForm>
    );
    expect(wrapper.find(VeasyForm)).toHaveLength(1);
    expect(wrapper.props().schema).toEqual(mockSchema);
    expect(wrapper.props().allState).toEqual(mockComponent.state);
    expect(wrapper.props().update).toEqual(mockComponent.setState);
  });

  test('Should render a section as container with a onChange function', () => {
    const wrapper = mount(
      <VeasyForm
        schema={mockSchema}
        allState={mockComponent.state}
        update={mockComponent.setState}
      >
        <Input name="title" />
        <input />
        <input />
      </VeasyForm>
    );
    expect(wrapper.find('form')).toHaveLength(1);
    expect(wrapper.find('section')).toHaveLength(0);
    expect(typeof wrapper.find('form').prop('onChange')).toEqual('function');
    wrapper.find('form').prop('onChange')({
      preventDefault: () => {},
      target: mockTarget
    });
  });

  test('Should render 3 inputs as children of section', () => {
    const wrapper = shallow(
      <VeasyForm
        schema={mockSchema}
        allState={mockComponent.state}
        update={mockComponent.setState}
      >
        <input />
        <input />
        <input />
      </VeasyForm>
    );
    const children = wrapper.find('form').children();
    expect(children).toHaveLength(3);
    expect(children.find('input')).toHaveLength(3);
  });

  test('The 1st children should have extra props', () => {
    const wrapper = shallow(
      <VeasyForm
        schema={mockSchema}
        allState={mockComponent.state}
        update={mockComponent.setState}
      >
        <Input name="title" />
        <input />
        <input />
      </VeasyForm>
    );
    const targetInput = wrapper.find('Input').at(0);
    expect(targetInput.prop('name')).toEqual('title');
    expect(targetInput.prop('status')).toEqual('normal');
    expect(targetInput.prop('errorText')).toEqual('');
    expect(targetInput.prop('value')).toEqual('');
  });

  test('The 2nd and 3rd children shouldn`t have extra props', () => {
    const wrapper = shallow(
      <VeasyForm
        schema={mockSchema}
        allState={mockComponent.state}
        update={mockComponent.setState}
      >
        <Input name="title" />
        <input />
        <input />
      </VeasyForm>
    );
    const targetInput = wrapper.find('input').at(1);
    expect(targetInput.prop('name')).toBe(undefined);
    expect(targetInput.prop('status')).toBe(undefined);
    expect(targetInput.prop('errorText')).toBe(undefined);
    expect(targetInput.prop('value')).toBe(undefined);

    const targetInput1 = wrapper.find('input').at(2);
    expect(targetInput1.prop('name')).toBe(undefined);
    expect(targetInput1.prop('status')).toBe(undefined);
    expect(targetInput1.prop('errorText')).toBe(undefined);
    expect(targetInput1.prop('value')).toBe(undefined);
  });

  test('Should bind recursive element', () => {
    const wrapper = shallow(componentToRender);
    const targetInput = wrapper.find('Input').at(0);
    expect(targetInput.prop('name')).toBe('title');
    expect(targetInput.prop('status')).toBe('normal');
    expect(targetInput.prop('errorText')).toBe('');
    expect(targetInput.prop('value')).toBe('');
  });

  test('Should maintain the user`s prop', () => {
    const wrapper = shallow(componentToRender);
    const targetInput = wrapper.find('Email').at(0);
    expect(targetInput.prop('super')).toBe('ok');
    expect(targetInput.prop('cool')).toBe('true');
  });

  test('trigger onBlur should invoke triggerValidation', () => {
    const wrapper = shallow(componentToRender);
    const mockTrigger = jest.fn();
    wrapper.instance().triggerValidation = mockTrigger;
    expect(mockTrigger.mock.calls.length).toBe(0);
    const target = wrapper.find('form').at(0);
    target.simulate('blur');
    expect(mockTrigger.mock.calls.length).toBe(1);
  });

  test('should trigger lib.resetForm', () => {
    const mockReset = jest.fn();
    const mockUpdate = jest.fn();
    lib.resetForm = mockReset;
    const wrapper = shallow(
      <VeasyForm
        schema={mockSchema}
        allState={mockComponent.state}
        update={mockUpdate}
      >

            <Input name="title" />

        <input />

          <Email super="ok" cool="true" />

      </VeasyForm>
    );
    const target = wrapper.find('form').at(0);
    target.simulate('reset', { preventDefault: () => {} });
    expect(mockReset.mock.calls.length).toBe(1);
    expect(mockReset).toBeCalledWith({
      title: {
        default: '',
        minLength: 1
      }
    });
    expect(mockUpdate.mock.calls.length).toBe(1);
  });

  test('Should render a form with 3 inputs with extra props', () => {
    const wrapper = shallow(
      <VeasyForm
        schema={mockSchema}
        allState={mockComponent.state}
        update={mockComponent.setState}
        name='super'
        action='google'
        onSubmit={() => {}}
      >
        <Input name="title" />
        <input />
        <input />
      </VeasyForm>
    );
    const form = wrapper.find('form').at(0);
    expect(form.prop('name')).toEqual('super');
    expect(form.prop('action')).toEqual('google');
    expect(typeof form.prop('onSubmit')).toEqual('function');
  });
});
<|endoftext|>


<commit_before>//only method, sub and unsub are valid messages
//so following fields would only required
var WAITON_MESSAGE_FIELDS = ['msg', 'id', 'method', 'name'];

wrapSession = function(sessionProto) {

  //store the currently running DDP message per session
  //we have to add this to the top of the waiting message
  var currentlyProcessingDDPMessage = {};

  var originalProcessMessage = sessionProto.processMessage;
  sessionProto.processMessage = function(msg) {
    if(Kadira.connected) {
      //only add kadiraInfo if it is connected
      var kadiraInfo = {
        session: this.id,
        userId: this.userId
      };

      if(msg.msg == 'method' || msg.msg == 'sub') {
        kadiraInfo.trace = Kadira.tracer.start(this, msg);

        var waitOnMessages = this.inQueue.map(function(msg) {
          return _.pick(msg, WAITON_MESSAGE_FIELDS);
        });

        //add currently processing ddp message if exists
        if(this.workerRunning) {
          waitOnMessages.unshift(_.pick(currentlyProcessingDDPMessage[this.id], WAITON_MESSAGE_FIELDS));
        }

        //use JSON stringify to save the CPU
        var startData = { userId: this.userId, params: JSON.stringify(msg.params) };
        Kadira.tracer.event(kadiraInfo.trace, 'start', startData);
        var waitEventId = Kadira.tracer.event(kadiraInfo.trace, 'wait', {waitOn: waitOnMessages}, kadiraInfo);
        msg._waitEventId = waitEventId;
        msg.__kadiraInfo = kadiraInfo;

        if(msg.msg == 'sub') {
          // start tracking inside processMessage allows us to indicate
          // wait time as well
          Kadira.models.pubsub._trackSub(this, msg);
        }
      }
    }

    return originalProcessMessage.call(this, msg);
  };

  //adding the method context to the current fiber
  var originalMethodHandler = sessionProto.protocol_handlers.method;
  sessionProto.protocol_handlers.method = function(msg, unblock) {
    currentlyProcessingDDPMessage[this.id] = msg;
    //add context
    var kadiraInfo = msg.__kadiraInfo;
    Kadira._setInfo(kadiraInfo);

    Kadira.tracer.eventEnd(kadiraInfo.trace, msg._waitEventId);

    return originalMethodHandler.call(this, msg, unblock);
  };

  //to capture the currently processing message
  var orginalSubHandler = sessionProto.protocol_handlers.sub;
  sessionProto.protocol_handlers.sub = function(msg, unblock) {
    currentlyProcessingDDPMessage[this.id] = msg;

    //add context
    var kadiraInfo = msg.__kadiraInfo;
    Kadira._setInfo(kadiraInfo);

    Kadira.tracer.eventEnd(kadiraInfo.trace, msg._waitEventId);

    return orginalSubHandler.call(this, msg, unblock);
  };

  //to capture the currently processing message
  var orginalUnSubHandler = sessionProto.protocol_handlers.unsub;
  sessionProto.protocol_handlers.unsub = function(msg, unblock) {
    currentlyProcessingDDPMessage[this.id] = msg;
    return orginalUnSubHandler.call(this, msg, unblock);
  };

  //we need to clear currentlyProcessingDDPMessage just after the session destroyed
  //otherwise, it will leads to a potential memory leaks
  var originalDestroy = sessionProto.destroy;
  sessionProto.destroy = function() {
    delete currentlyProcessingDDPMessage[this.id];
    return originalDestroy.call(this);
  };

  //track method ending (to get the result of error)
  var originalSend = sessionProto.send;
  sessionProto.send = function(msg) {
    if(msg.msg == 'result') {
      var kadiraInfo = Kadira._getInfo();
      if(msg.error) {
        var error = msg.error;

        //pick the error from the __kadiraInfo if setted with
        //DDPServer._CurrentWriteFence.withValue hijack
        if(kadiraInfo && kadiraInfo.currentError) {
          error = kadiraInfo.currentError;
        }

        error = _.pick(error, ['message', 'stack']);

        Kadira.tracer.endLastEvent(kadiraInfo.trace);
        Kadira.tracer.event(kadiraInfo.trace, 'error', {error: error});
      } else {
        var isForced = Kadira.tracer.endLastEvent(kadiraInfo.trace);
        if (isForced) {
          console.warn('Kadira endevent forced complete', JSON.stringify(kadiraInfo.trace.events));
        };
        Kadira.tracer.event(kadiraInfo.trace, 'complete');
      }

      if(kadiraInfo) {
        //processing the message
        var trace = Kadira.tracer.buildTrace(kadiraInfo.trace);
        Kadira.models.error.trackError(error, trace);
        Kadira.models.methods.processMethod(trace);

        //clean and make sure, fiber is clean
        //not sure we need to do this, but a preventive measure
        Kadira._setInfo(null);
      }
    }

    return originalSend.call(this, msg);
  };

  //for the pub/sub data-impact calculation
  ['sendAdded', 'sendChanged', 'sendRemoved'].forEach(function(funcName) {
    var originalFunc = sessionProto[funcName];
    sessionProto[funcName] = function(collectionName, id, fields) {
      //fields is not relevant for `sendRemoved`, but does make any harm
      var eventName = funcName.substring(4).toLowerCase();
      var subscription = Kadira.env.currentSub.get();

      if(subscription) {
        var session = this;
        Kadira.models.pubsub._trackNetworkImpact(session, subscription, eventName, collectionName, id, fields);
      }

      return originalFunc.call(this, collectionName, id, fields);
    };
  });
};

// wrap existing method handlers for capturing errors
_.each(Meteor.default_server.method_handlers, function(handler, name) {
  wrapMethodHanderForErrors(name, handler, Meteor.default_server.method_handlers);
});

// wrap future method handlers for capturing errors
var originalMeteorMethods = Meteor.methods;
Meteor.methods = function(methodMap) {
  _.each(methodMap, function(handler, name) {
    wrapMethodHanderForErrors(name, handler, methodMap);
  });
  originalMeteorMethods(methodMap);
};

function wrapMethodHanderForErrors(name, originalHandler, methodMap) {
  methodMap[name] = function() {
    try{
      return originalHandler.apply(this, arguments);
    } catch(ex) {

      if(Kadira._getInfo()) {
        Kadira._getInfo().currentError = ex;
      }
      throw ex;
    }
  }
}
<commit_msg>only trackErrors this there is an error (for methods)<commit_after>//only method, sub and unsub are valid messages
//so following fields would only required
var WAITON_MESSAGE_FIELDS = ['msg', 'id', 'method', 'name'];

wrapSession = function(sessionProto) {

  //store the currently running DDP message per session
  //we have to add this to the top of the waiting message
  var currentlyProcessingDDPMessage = {};

  var originalProcessMessage = sessionProto.processMessage;
  sessionProto.processMessage = function(msg) {
    if(Kadira.connected) {
      //only add kadiraInfo if it is connected
      var kadiraInfo = {
        session: this.id,
        userId: this.userId
      };

      if(msg.msg == 'method' || msg.msg == 'sub') {
        kadiraInfo.trace = Kadira.tracer.start(this, msg);

        var waitOnMessages = this.inQueue.map(function(msg) {
          return _.pick(msg, WAITON_MESSAGE_FIELDS);
        });

        //add currently processing ddp message if exists
        if(this.workerRunning) {
          waitOnMessages.unshift(_.pick(currentlyProcessingDDPMessage[this.id], WAITON_MESSAGE_FIELDS));
        }

        //use JSON stringify to save the CPU
        var startData = { userId: this.userId, params: JSON.stringify(msg.params) };
        Kadira.tracer.event(kadiraInfo.trace, 'start', startData);
        var waitEventId = Kadira.tracer.event(kadiraInfo.trace, 'wait', {waitOn: waitOnMessages}, kadiraInfo);
        msg._waitEventId = waitEventId;
        msg.__kadiraInfo = kadiraInfo;

        if(msg.msg == 'sub') {
          // start tracking inside processMessage allows us to indicate
          // wait time as well
          Kadira.models.pubsub._trackSub(this, msg);
        }
      }
    }

    return originalProcessMessage.call(this, msg);
  };

  //adding the method context to the current fiber
  var originalMethodHandler = sessionProto.protocol_handlers.method;
  sessionProto.protocol_handlers.method = function(msg, unblock) {
    currentlyProcessingDDPMessage[this.id] = msg;
    //add context
    var kadiraInfo = msg.__kadiraInfo;
    Kadira._setInfo(kadiraInfo);

    Kadira.tracer.eventEnd(kadiraInfo.trace, msg._waitEventId);

    return originalMethodHandler.call(this, msg, unblock);
  };

  //to capture the currently processing message
  var orginalSubHandler = sessionProto.protocol_handlers.sub;
  sessionProto.protocol_handlers.sub = function(msg, unblock) {
    currentlyProcessingDDPMessage[this.id] = msg;

    //add context
    var kadiraInfo = msg.__kadiraInfo;
    Kadira._setInfo(kadiraInfo);

    Kadira.tracer.eventEnd(kadiraInfo.trace, msg._waitEventId);

    return orginalSubHandler.call(this, msg, unblock);
  };

  //to capture the currently processing message
  var orginalUnSubHandler = sessionProto.protocol_handlers.unsub;
  sessionProto.protocol_handlers.unsub = function(msg, unblock) {
    currentlyProcessingDDPMessage[this.id] = msg;
    return orginalUnSubHandler.call(this, msg, unblock);
  };

  //we need to clear currentlyProcessingDDPMessage just after the session destroyed
  //otherwise, it will leads to a potential memory leaks
  var originalDestroy = sessionProto.destroy;
  sessionProto.destroy = function() {
    delete currentlyProcessingDDPMessage[this.id];
    return originalDestroy.call(this);
  };

  //track method ending (to get the result of error)
  var originalSend = sessionProto.send;
  sessionProto.send = function(msg) {
    if(msg.msg == 'result') {
      var kadiraInfo = Kadira._getInfo();
      if(msg.error) {
        var error = msg.error;

        //pick the error from the __kadiraInfo if setted with
        //DDPServer._CurrentWriteFence.withValue hijack
        if(kadiraInfo && kadiraInfo.currentError) {
          error = kadiraInfo.currentError;
        }

        error = _.pick(error, ['message', 'stack']);

        Kadira.tracer.endLastEvent(kadiraInfo.trace);
        Kadira.tracer.event(kadiraInfo.trace, 'error', {error: error});
      } else {
        var isForced = Kadira.tracer.endLastEvent(kadiraInfo.trace);
        if (isForced) {
          console.warn('Kadira endevent forced complete', JSON.stringify(kadiraInfo.trace.events));
        };
        Kadira.tracer.event(kadiraInfo.trace, 'complete');
      }

      if(kadiraInfo) {
        //processing the message
        var trace = Kadira.tracer.buildTrace(kadiraInfo.trace);
        Kadira.models.methods.processMethod(trace);
        if(error) {
          Kadira.models.error.trackError(error, trace);
        }

        //clean and make sure, fiber is clean
        //not sure we need to do this, but a preventive measure
        Kadira._setInfo(null);
      }
    }

    return originalSend.call(this, msg);
  };

  //for the pub/sub data-impact calculation
  ['sendAdded', 'sendChanged', 'sendRemoved'].forEach(function(funcName) {
    var originalFunc = sessionProto[funcName];
    sessionProto[funcName] = function(collectionName, id, fields) {
      //fields is not relevant for `sendRemoved`, but does make any harm
      var eventName = funcName.substring(4).toLowerCase();
      var subscription = Kadira.env.currentSub.get();

      if(subscription) {
        var session = this;
        Kadira.models.pubsub._trackNetworkImpact(session, subscription, eventName, collectionName, id, fields);
      }

      return originalFunc.call(this, collectionName, id, fields);
    };
  });
};

// wrap existing method handlers for capturing errors
_.each(Meteor.default_server.method_handlers, function(handler, name) {
  wrapMethodHanderForErrors(name, handler, Meteor.default_server.method_handlers);
});

// wrap future method handlers for capturing errors
var originalMeteorMethods = Meteor.methods;
Meteor.methods = function(methodMap) {
  _.each(methodMap, function(handler, name) {
    wrapMethodHanderForErrors(name, handler, methodMap);
  });
  originalMeteorMethods(methodMap);
};

function wrapMethodHanderForErrors(name, originalHandler, methodMap) {
  methodMap[name] = function() {
    try{
      return originalHandler.apply(this, arguments);
    } catch(ex) {

      if(Kadira._getInfo()) {
        Kadira._getInfo().currentError = ex;
      }
      throw ex;
    }
  }
}
<|endoftext|>


<commit_before>/**
 * Create mappings between the names parsed from useragent to caniuse
 * name equivalents.  We support the caniuse names listed in the
 * `baselineVersion` map below.  This module must return one of these
 * family names, with a version matching the baseline range, for any
 * polyfills to be served.
 *
 * Multiple names may map to the same canonical caniuse family
 */

'use strict';

const useragent = require('useragent');
const MAX_UA_LEN = 200;

// Use a local LRU cache rather than the one built into useragent, since that cannot be configured
const cache = require('lru-cache')({
	max: 5000
});

const baseLineVersions = {
	"ie": ">=6",
	"ie_mob": ">=8",
	"chrome": "*",
	"safari": ">=3",
	"ios_saf": ">=3",
	"ios_chr": ">=3",
	"firefox": ">=3.6",
	"firefox_mob": ">=4",
	"android": ">=2.3",
	"opera": ">=11",
	"op_mob": ">=10",
	"op_mini": ">=5",
	"bb": ">=6"
};

/* Aliases may be expressed in three formats:
    1. <detectedfamily>: <targetfamily>
       The browser family is renamed, version is maintained
    2. <detectedfamily>: [<targetfamily>, <targetmajor>, <targetminor>, <targetpatch>]
       The browser family is renamed and the version is set to the one specified
    3. <detectedfamily>: { <semver>: [<targetfamily>, <targetmajor>, <targetminor>, <targetpatch>], ... }
       Specific version ranges of this family are mapped to the specified browsers
*/
const aliases = {
	"blackberry webkit": "bb",
	"blackberry": "bb",

	"pale moon (firefox variant)": "firefox",
	"firefox mobile": "firefox_mob",
	"firefox namoroka": "firefox",
	"firefox shiretoko": "firefox",
	"firefox minefield": "firefox",
	"firefox alpha": "firefox",
	"firefox beta": "firefox",
	"microb": "firefox",
	"mozilladeveloperpreview": "firefox",
	"iceweasel": "firefox",

	"opera tablet": "opera",

	"opera mobile": "op_mob",
	"opera mini": "op_mini",

	"chrome mobile": "chrome",
	"chrome frame": "chrome",
	"chromium": "chrome",

	"ie mobile": "ie_mob",

	"ie large screen": "ie",
	"internet explorer": "ie",
	"edge": "ie",
	"edge mobile": "ie",

	"chrome mobile ios": "ios_chr",

	"mobile safari": "ios_saf",
	"iphone": "ios_saf",
	"iphone simulator": "ios_saf",
	"mobile safari uiwebview": "ios_saf",

	"phantomjs": ["safari", 5],

	"yandex browser": {
		"14.10": ["chrome", 37],
		"14.8": ["chrome", 36],
		"14.7": ["chrome", 35],
		"14.5": ["chrome", 34],
		"14.4": ["chrome", 33],
		"14.2": ["chrome", 32],
		"13.12": ["chrome", 30],
		"13.10": ["chrome", 28]
	}
};

// Chrome and Opera on iOS uses a UIWebView of the underlying platform to render
// content, by stripping the CriOS or OPiOS strings the useragent parser will alias the
// user agent to ios_saf for the UIWebView, which is closer to the actual
// renderer
function stripiOSWebViewBrowsers(uaString) {
	return uaString.replace(/((CriOS|OPiOS)\/(\d+)\.(\d+)\.(\d+)\.(\d+)|(FxiOS\/(\d+)\.(\d+)))/, '');
}

function UA(uaString) {

	// Limit the length of the UA to avoid perf issues in UA parsing
	uaString = uaString.substr(0, MAX_UA_LEN);

	// The longest string that can possibly be a normalized browser name that we
	// support is XXXXXXXXXX/###.###.### (22 chars), so avoid doing the regex if
	// the input string is longer than that
	let normalized = (uaString.length < 22) && uaString.match(/^(\w+)\/(\d+)(?:\.(\d+)(?:\.(\d+))?)?$/i);
	if (!normalized) {
		normalized = cache.get(uaString);
	}
	if (normalized) {
		this.ua = new useragent.Agent(normalized[1], normalized[2], (normalized[3] || 0), (normalized[4] || 0));
	} else {
		this.ua = useragent.parse(stripiOSWebViewBrowsers(uaString));

		// For improved CDN cache performance, remove the patch version.  There are few cases in which a patch release drops the requirement for a polyfill, but if so, the polyfill can simply be served unnecessarily to the patch versions that contain the fix, and we can stop targeting at the next minor release.
		this.ua.patch = '0';

		// Resolve aliases
		this.ua.family = this.ua.family.toLowerCase();
		if (aliases[this.ua.family]) {

			// Map to different family, use same version scheme
			if (typeof aliases[this.ua.family] === 'string') {
				this.ua.family = aliases[this.ua.family];

			// Map to different family with constant version
			} else if (Array.isArray(aliases[this.ua.family]) && aliases[this.ua.family].length >= 2) {
				const a = aliases[this.ua.family];
				this.ua = new useragent.Agent(a[0], a[1], (a[2] || 0), (a[3] || 0));

			// Map to different family with per-version mapping
			} else if (typeof aliases[this.ua.family] === 'object') {
				for (const semverExpr in aliases[this.ua.family]) {
					if (this.ua.satisfies(semverExpr) && Array.isArray(aliases[this.ua.family][semverExpr])) {
						const a = aliases[this.ua.family][semverExpr];
						this.ua = new useragent.Agent(a[0], a[1], (a[2] || 0), (a[3] || 0));
						break;
					}
				}
			}
		}
		cache.set(uaString, ["", this.ua.family, this.ua.major, this.ua.minor, this.ua.patch]);
	}
}

UA.prototype.getFamily = function() {
	return this.ua.family;
};

UA.prototype.getVersion = function() {
	return this.ua.toVersion();
};

UA.prototype.satisfies = function() {
	return (
		this.ua.satisfies.apply(this.ua, arguments) &&
		this.ua.family in baseLineVersions &&
		this.ua.satisfies(baseLineVersions[this.ua.family])
	);
};
UA.prototype.getBaseline = function() {
	return baseLineVersions[this.ua.family];
};
UA.prototype.meetsBaseline = function() {
	return (this.ua.satisfies(baseLineVersions[this.ua.family]));
};
UA.prototype.isUnknown = function() {
	return (Object.keys(baseLineVersions).indexOf(this.ua.family) === -1);
};

UA.normalize = function(uaString) {
	if (uaString.match(/^\w+\/\d+(\.\d+(\.\d+)?)?$/i)) {
		return uaString.toLowerCase();
	}
	const ua = new UA(uaString);
	return ua ? ua.getFamily() + '/' + ua.getVersion() : undefined;
};

UA.getBaselines = function() {
	return baseLineVersions;
};

module.exports = UA;
<commit_msg>Incorrectly triggers eslint warning for let.  Const does not allow the value to change.<commit_after>/**
 * Create mappings between the names parsed from useragent to caniuse
 * name equivalents.  We support the caniuse names listed in the
 * `baselineVersion` map below.  This module must return one of these
 * family names, with a version matching the baseline range, for any
 * polyfills to be served.
 *
 * Multiple names may map to the same canonical caniuse family
 */

'use strict';

const useragent = require('useragent');
const MAX_UA_LEN = 200;

// Use a local LRU cache rather than the one built into useragent, since that cannot be configured
const cache = require('lru-cache')({
	max: 5000
});

const baseLineVersions = {
	"ie": ">=6",
	"ie_mob": ">=8",
	"chrome": "*",
	"safari": ">=3",
	"ios_saf": ">=3",
	"ios_chr": ">=3",
	"firefox": ">=3.6",
	"firefox_mob": ">=4",
	"android": ">=2.3",
	"opera": ">=11",
	"op_mob": ">=10",
	"op_mini": ">=5",
	"bb": ">=6"
};

/* Aliases may be expressed in three formats:
    1. <detectedfamily>: <targetfamily>
       The browser family is renamed, version is maintained
    2. <detectedfamily>: [<targetfamily>, <targetmajor>, <targetminor>, <targetpatch>]
       The browser family is renamed and the version is set to the one specified
    3. <detectedfamily>: { <semver>: [<targetfamily>, <targetmajor>, <targetminor>, <targetpatch>], ... }
       Specific version ranges of this family are mapped to the specified browsers
*/
const aliases = {
	"blackberry webkit": "bb",
	"blackberry": "bb",

	"pale moon (firefox variant)": "firefox",
	"firefox mobile": "firefox_mob",
	"firefox namoroka": "firefox",
	"firefox shiretoko": "firefox",
	"firefox minefield": "firefox",
	"firefox alpha": "firefox",
	"firefox beta": "firefox",
	"microb": "firefox",
	"mozilladeveloperpreview": "firefox",
	"iceweasel": "firefox",

	"opera tablet": "opera",

	"opera mobile": "op_mob",
	"opera mini": "op_mini",

	"chrome mobile": "chrome",
	"chrome frame": "chrome",
	"chromium": "chrome",

	"ie mobile": "ie_mob",

	"ie large screen": "ie",
	"internet explorer": "ie",
	"edge": "ie",
	"edge mobile": "ie",

	"chrome mobile ios": "ios_chr",

	"mobile safari": "ios_saf",
	"iphone": "ios_saf",
	"iphone simulator": "ios_saf",
	"mobile safari uiwebview": "ios_saf",

	"phantomjs": ["safari", 5],

	"yandex browser": {
		"14.10": ["chrome", 37],
		"14.8": ["chrome", 36],
		"14.7": ["chrome", 35],
		"14.5": ["chrome", 34],
		"14.4": ["chrome", 33],
		"14.2": ["chrome", 32],
		"13.12": ["chrome", 30],
		"13.10": ["chrome", 28]
	}
};

// Chrome and Opera on iOS uses a UIWebView of the underlying platform to render
// content, by stripping the CriOS or OPiOS strings the useragent parser will alias the
// user agent to ios_saf for the UIWebView, which is closer to the actual
// renderer
function stripiOSWebViewBrowsers(uaString) {
	return uaString.replace(/((CriOS|OPiOS)\/(\d+)\.(\d+)\.(\d+)\.(\d+)|(FxiOS\/(\d+)\.(\d+)))/, '');
}

function UA(uaString) {

	// Limit the length of the UA to avoid perf issues in UA parsing
	uaString = uaString.substr(0, MAX_UA_LEN);

	// The longest string that can possibly be a normalized browser name that we
	// support is XXXXXXXXXX/###.###.### (22 chars), so avoid doing the regex if
	// the input string is longer than that
	let normalized = (uaString.length < 22) && uaString.match(/^(\w+)\/(\d+)(?:\.(\d+)(?:\.(\d+))?)?$/i);
	if (!normalized) {
		normalized = cache.get(uaString);
	}
	if (normalized) {
		this.ua = new useragent.Agent(normalized[1], normalized[2], (normalized[3] || 0), (normalized[4] || 0));
	} else {
		this.ua = useragent.parse(stripiOSWebViewBrowsers(uaString));

		// For improved CDN cache performance, remove the patch version.  There are few cases in which a patch release drops the requirement for a polyfill, but if so, the polyfill can simply be served unnecessarily to the patch versions that contain the fix, and we can stop targeting at the next minor release.
		this.ua.patch = '0';

		// Resolve aliases
		this.ua.family = this.ua.family.toLowerCase();
		if (aliases[this.ua.family]) {

			// Map to different family, use same version scheme
			if (typeof aliases[this.ua.family] === 'string') {
				this.ua.family = aliases[this.ua.family];

			// Map to different family with constant version
			} else if (Array.isArray(aliases[this.ua.family]) && aliases[this.ua.family].length >= 2) {
				const a = aliases[this.ua.family];
				this.ua = new useragent.Agent(a[0], a[1], (a[2] || 0), (a[3] || 0));

			// Map to different family with per-version mapping
			} else if (typeof aliases[this.ua.family] === 'object') {
				for (let semverExpr in aliases[this.ua.family]) {
					if (this.ua.satisfies(semverExpr) && Array.isArray(aliases[this.ua.family][semverExpr])) {
						const a = aliases[this.ua.family][semverExpr];
						this.ua = new useragent.Agent(a[0], a[1], (a[2] || 0), (a[3] || 0));
						break;
					}
				}
			}
		}
		cache.set(uaString, ["", this.ua.family, this.ua.major, this.ua.minor, this.ua.patch]);
	}
}

UA.prototype.getFamily = function() {
	return this.ua.family;
};

UA.prototype.getVersion = function() {
	return this.ua.toVersion();
};

UA.prototype.satisfies = function() {
	return (
		this.ua.satisfies.apply(this.ua, arguments) &&
		this.ua.family in baseLineVersions &&
		this.ua.satisfies(baseLineVersions[this.ua.family])
	);
};
UA.prototype.getBaseline = function() {
	return baseLineVersions[this.ua.family];
};
UA.prototype.meetsBaseline = function() {
	return (this.ua.satisfies(baseLineVersions[this.ua.family]));
};
UA.prototype.isUnknown = function() {
	return (Object.keys(baseLineVersions).indexOf(this.ua.family) === -1);
};

UA.normalize = function(uaString) {
	if (uaString.match(/^\w+\/\d+(\.\d+(\.\d+)?)?$/i)) {
		return uaString.toLowerCase();
	}
	const ua = new UA(uaString);
	return ua ? ua.getFamily() + '/' + ua.getVersion() : undefined;
};

UA.getBaselines = function() {
	return baseLineVersions;
};

module.exports = UA;
<|endoftext|>


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

function Player(player, queue) {
    this.player = player;
    this.isPlaying = false;
    this.queue = queue;

    events.EventEmitter.call(this);

    queue.on("change", function () {
        var newTrack = queue.getCurrentTrack()
        if (newTrack != this.currentTrack) {
            if (newTrack == null) {
                this.stop();
                this.currentTrack = null;
                this.changedCurrent();
            } else {
                this.playTrack(newTrack);
            }
        }
    }.bind(this));
}

Player.prototype = {
    playTrack: function (track) {
        this.player.stop();
        this.queue.loadTrack(track).then(function (track) {
            if ("undefined" === typeof track) {
                this.isPlaying = false;
                this.emit("playState", this.isPlaying)
                this.changedCurrent();
                this.stop();
                return;
            }
            try {
                this.currentTrack = track;
                console.log('Playing: %s - %s', track.artists[0].name, track.name);
                this.player.play(track);
                this.isPlaying = true;
                this.emit("playState", this.isPlaying)
                this.changedCurrent();
            } catch (e) {
                console.error(e);
                this.emit("playError", {e: e, track: this.getCurrentTrack()});
                this.queue.next();
            }
        }.bind(this));
    },
    getPlayingTrack: function () {
        return this.currentTrack;
    },
    play: function () {
        this.resume();
        this.isPlaying = true;
        this.emit("playState", this.isPlaying)
    },
    pause: function () {
        this.player.pause();
        this.isPlaying = false;
        this.emit("playState", this.isPlaying)
    },
    resume: function () {
        this.player.resume();
        this.isPlaying = false;
        this.emit("playState", this.isPlaying)
    },
    stop: function () {
        this.player.seek(0);
        this.player.stop();
        this.currentTrack = null;
        this.isPlaying = false;
        this.emit("playState", this.isPlaying)
    },
    changedCurrent: function () {
        this.emit("changedCurrent", this.getPlayingTrack() == null ? null : this.getPlayingTrack().getJson());
    }
}

Player.prototype.__proto__ = events.EventEmitter.prototype;

module.exports = Player;<commit_msg>removed unneeded (and broken) function<commit_after>var events = require('events');

function Player(player, queue) {
    this.player = player;
    this.isPlaying = false;
    this.queue = queue;

    events.EventEmitter.call(this);

    queue.on("change", function () {
        var newTrack = queue.getCurrentTrack()
        if (newTrack != this.currentTrack) {
            if (newTrack == null) {
                this.stop();
                this.currentTrack = null;
                this.changedCurrent();
            } else {
                this.playTrack(newTrack);
            }
        }
    }.bind(this));
}

Player.prototype = {
    playTrack: function (track) {
        this.player.stop();
        this.queue.loadTrack(track).then(function (track) {
            if ("undefined" === typeof track) {
                this.isPlaying = false;
                this.emit("playState", this.isPlaying)
                this.changedCurrent();
                this.stop();
                return;
            }
            try {
                this.currentTrack = track;
                console.log('Playing: %s - %s', track.artists[0].name, track.name);
                this.player.play(track);
                this.isPlaying = true;
                this.emit("playState", this.isPlaying)
                this.changedCurrent();
            } catch (e) {
                console.error(e);
                this.emit("playError", {e: e, track: this.getCurrentTrack()});
                this.queue.next();
            }
        }.bind(this));
    },
    getPlayingTrack: function () {
        return this.currentTrack;
    },
    play: function () {
        this.player.resume();
        this.isPlaying = true;
        this.emit("playState", this.isPlaying)
    },
    pause: function () {
        this.player.pause();
        this.isPlaying = false;
        this.emit("playState", this.isPlaying)
    },
    stop: function () {
        this.player.seek(0);
        this.player.stop();
        this.currentTrack = null;
        this.isPlaying = false;
        this.emit("playState", this.isPlaying)
    },
    changedCurrent: function () {
        this.emit("changedCurrent", this.getPlayingTrack() == null ? null : this.getPlayingTrack().getJson());
    }
}

Player.prototype.__proto__ = events.EventEmitter.prototype;

module.exports = Player;<|endoftext|>


<commit_before>import Bacon from 'baconjs'
import R from 'ramda'

let isSuccessOrFailed = R.contains(R.__, ['Success', 'Failed'])
let isSuccess = R.equals('Success')
let isBuilding = R.equals('Building')

export default function startBuildOrTakeRunning(buildSetting, tutum, tutumStream) {
  return Bacon.once(buildSetting)
    .flatMap((buildSetting) => {
      if (isBuilding(buildSetting.state)) {
        return buildSetting
      }

      return startBuild(buildSetting, tutum)
    })
    .flatMap((buildSetting) => {
      return getBuildFinished(buildSetting, tutumStream)
    })
}

function startBuild(buildSetting, tutum) {
  return Bacon.fromPromise(tutum.buildTag({
    uri: buildSetting.resource_uri
  }))
}

function getBuildFinished(buildSetting, tutumStream) {
  return tutumStream
    .filter((event) => event.resource_uri == buildSetting.resource_uri)
    .filter((event) => isSuccessOrFailed(event.state))
    .flatMap((event) => Bacon.once(isSuccess(event.state)))
    .first()
}
<commit_msg>Log some more in start build stream<commit_after>import Bacon from 'baconjs'
import R from 'ramda'
import colors from 'colors'

let debug = require('debug')('tutum-tagger')

let isSuccessOrFailed = R.contains(R.__, ['Success', 'Failed'])
let isSuccess = R.equals('Success')
let isBuilding = R.equals('Building')

export default function startBuildOrTakeRunning(buildSetting, tutum, tutumStream) {
  return Bacon.once(buildSetting)
    .flatMap((buildSetting) => {
      if (isBuilding(buildSetting.state)) {
        debug(`There's a build for ${buildSetting.resource_uri.cyan} running already.`)

        return buildSetting
      }

      debug(`Starting a new build of ${buildSetting.resource_uri.cyan}.`)

      return startBuild(buildSetting, tutum)
    })
    .flatMap((buildSetting) => getBuildFinished(buildSetting, tutumStream))
}

function startBuild(buildSetting, tutum) {
  return Bacon.fromPromise(tutum.buildTag({
    uri: buildSetting.resource_uri
  }))
}

function getBuildFinished(buildSetting, tutumStream) {
  return tutumStream
    .filter((event) => event.resource_uri == buildSetting.resource_uri)
    .doAction((event) => debug(`Got an event for ${event.resource_uri.cyan} and state ${event.state.cyan}.`))
    .filter((event) => isSuccessOrFailed(event.state))
    .flatMap((event) => Bacon.once(isSuccess(event.state)))
    .doAction((isSuccessful) => debug(`Build ${isSuccessful ? 'is'.green : 'is not'.red} successful.`))
    .first()
}
<|endoftext|>


<commit_before>'use strict';

var YAML = require('./yaml'),
    util = require('./util'),
    ono  = require('ono');

module.exports = parse;

/**
 * Parses the given data as YAML, JSON, or a raw Buffer (byte array), depending on the options.
 *
 * @param {string|Buffer} data - The data to be parsed
 * @param {string} path - The file path or URL that `data` came from
 * @param {$RefParserOptions} options
 *
 * @returns {string|Buffer|object}
 * If `data` can be parsed as YAML or JSON, then the returned value is a JavaScript object.
 * Otherwise, the returned value is the raw string or Buffer that was passed in.
 */
function parse(data, path, options) {
  var parsed;

  try {
    if (options.allow.yaml) {
      util.debug('Parsing YAML file: %s', path);
      parsed = YAML.parse(data.toString());
      util.debug('    Parsed successfully');
    }
    else if (options.allow.json) {
      util.debug('Parsing JSON file: %s', path);
      parsed = JSON.parse(data.toString());
      util.debug('    Parsed successfully');
    }
    else {
      parsed = data;
    }
  }
  catch (e) {
    var ext = util.extname(path);
    if (options.allow.unknown && ['.json', '.yaml', '.yml'].indexOf(ext) === -1) {
      // It's not a YAML or JSON file, and unknown formats are allowed,
      // so ignore the parsing error and just return the raw data
      util.debug('    Unknown file format. Not parsed.');
      parsed = data;
    }
    else {
      throw ono.syntax(e, 'Error parsing "%s"', path);
    }
  }

  if (isEmpty(parsed) && !options.allow.empty) {
    throw ono.syntax('Error parsing "%s". \nParsed value is empty', path);
  }

  return parsed;
}

/**
 * Determines whether the parsed value is "empty".
 *
 * @param {*} value
 * @returns {boolean}
 */
function isEmpty(value) {
  return !value ||
    (typeof(value) === 'object' && Object.keys(value) === 0) ||
    (typeof(value) === 'string' && value.trim().length === 0) ||
    (value instanceof Buffer && value.length === 0);
}
<commit_msg>Refactored utility functions<commit_after>'use strict';

var YAML = require('./yaml'),
    util = require('./util'),
    ono  = require('ono');

module.exports = parse;

/**
 * Parses the given data as YAML, JSON, or a raw Buffer (byte array), depending on the options.
 *
 * @param {string|Buffer} data - The data to be parsed
 * @param {string} path - The file path or URL that `data` came from
 * @param {$RefParserOptions} options
 *
 * @returns {string|Buffer|object}
 * If `data` can be parsed as YAML or JSON, then the returned value is a JavaScript object.
 * Otherwise, the returned value is the raw string or Buffer that was passed in.
 */
function parse(data, path, options) {
  var parsed;

  try {
    if (options.allow.yaml) {
      util.debug('Parsing YAML file: %s', path);
      parsed = YAML.parse(data.toString());
      util.debug('    Parsed successfully');
    }
    else if (options.allow.json) {
      util.debug('Parsing JSON file: %s', path);
      parsed = JSON.parse(data.toString());
      util.debug('    Parsed successfully');
    }
    else {
      parsed = data;
    }
  }
  catch (e) {
    var ext = util.path.extname(path);
    if (options.allow.unknown && ['.json', '.yaml', '.yml'].indexOf(ext) === -1) {
      // It's not a YAML or JSON file, and unknown formats are allowed,
      // so ignore the parsing error and just return the raw data
      util.debug('    Unknown file format. Not parsed.');
      parsed = data;
    }
    else {
      throw ono.syntax(e, 'Error parsing "%s"', path);
    }
  }

  if (isEmpty(parsed) && !options.allow.empty) {
    throw ono.syntax('Error parsing "%s". \nParsed value is empty', path);
  }

  return parsed;
}

/**
 * Determines whether the parsed value is "empty".
 *
 * @param {*} value
 * @returns {boolean}
 */
function isEmpty(value) {
  return !value ||
    (typeof(value) === 'object' && Object.keys(value) === 0) ||
    (typeof(value) === 'string' && value.trim().length === 0) ||
    (value instanceof Buffer && value.length === 0);
}
<|endoftext|>


<commit_before>exports.it = exports.specify = it
exports.context = exports.describe = describe
exports.before = before
exports.after = after
exports.beforeEach = beforeEach
exports.afterEach = afterEach

exports.global = function () {
  Object.keys(exports).forEach(function (g) {
    global[g] = exports[g]
  })
}

var t = require('./tap.js')
t.jobs = 1
var tapStack = [ t ]
var level = 0
var suiteStack = []

var running = false
function run () {
  if (running)
    return
  running = true
  process.nextTick(function () {
    rootSuite.run()
  })
}

function describe (name, fn) {
  new Suite(name, fn)
}

function Suite (name, fn) {
  this.parent = suiteStack[ suiteStack.length - 1 ]
  if (typeof name === 'function')
    fn = name, name = null
  if (fn && fn.name && !name)
    name = fn.name
  this.todo = !fn
  this.fn = fn
  this.name = name
  this.after = []
  this.test = null

  this.run()
}

Suite.prototype.run = function () {
  var t = tapStack[ tapStack.length - 1 ]
  t.test(this.name, { todo: this.todo }, function (tt) {
    this.test = tt
    tapStack.push(tt)
    suiteStack.push(this)
    var ret = this.fn()
    this.runAfter()
    suiteStack.pop()
    return ret
  }.bind(this))
}

Suite.prototype.runAfter = function () {
  this.after.forEach(function (namefn) {
    var name = namefn[0]
    var fn = namefn[1]
    before(name, fn)
  })
  do {
    var t = tapStack.pop()
  } while (t && t !== this.test)
  if (this.test && !this.test.results)
    t.end()
}

function before (name, fn) {
  if (typeof name === 'function')
    fn = name, name = null
  if (fn && fn.name && !name)
    name = fn.name
  var todo = !fn
  var suite = suiteStack[ suiteStack.length - 1 ]
  var t = tapStack[ tapStack.length - 1 ]
  if (!name)
    name = ''
  t.test(name, { todo: todo, silent: true }, function (tt) {
    var ret = fn.call(suite, done(tt))
    if (!ret && fn.length === 0)
      tt.end()
    else
      return ret
  })

  function done (tt) { return function (er) {
    if (er)
      tt.threw(er)
    else
      tt.end()
  }}
}

function it (name, fn) {
  if (typeof name === 'function')
    fn = name, name = null
  if (fn && fn.name && !name)
    name = fn.name
  var todo = !fn
  var suite = suiteStack[ suiteStack.length - 1 ]
  var t = tapStack[ tapStack.length - 1 ]
  if (!name)
    name = ''
  t.test(name, { todo: todo, tapMochaTest: true }, function (tt) {
    var ret = fn.call(tt, done(tt))
    if (ret && ret.then)
      return ret
    else if (fn.length === 0)
      tt.end()
  })

  function done (tt) { return function (er) {
    if (er)
      tt.threw(er)
    else
      tt.end()
  }}
}

function after (name, fn) {
  var suite = suiteStack[ suiteStack.length - 1 ]
  if (!suite)
    throw new Error('cannot call "after" outside of describe()')
  if (fn)
    suite.after.push([name, fn])
  else
    suite.after.push([name])
}

function moment (when, fn) {
  var t = tapStack[ tapStack.length - 1 ]
  t[when](function (cb) {
    if (!this.options.tapMochaTest)
      return cb()
    var suite = suiteStack[ suiteStack.length - 1 ]
    var ret = fn.call(this, cb)
    if (ret && ret.then)
      return ret
    else if (fn.length === 0)
      return cb()
  })
}

function beforeEach (fn) {
  moment('beforeEach', fn)
}

function afterEach (fn) {
  moment('afterEach', fn)
}
<commit_msg>remove some dead code<commit_after>exports.it = exports.specify = it
exports.context = exports.describe = describe
exports.before = before
exports.after = after
exports.beforeEach = beforeEach
exports.afterEach = afterEach

exports.global = function () {
  Object.keys(exports).forEach(function (g) {
    global[g] = exports[g]
  })
}

var t = require('./tap.js')
t.jobs = 1
var tapStack = [ t ]
var level = 0
var suiteStack = []

function describe (name, fn) {
  new Suite(name, fn)
}

function Suite (name, fn) {
  this.parent = suiteStack[ suiteStack.length - 1 ]
  if (typeof name === 'function')
    fn = name, name = null
  if (fn && fn.name && !name)
    name = fn.name
  this.todo = !fn
  this.fn = fn
  this.name = name
  this.after = []
  this.test = null

  this.run()
}

Suite.prototype.run = function () {
  var t = tapStack[ tapStack.length - 1 ]
  t.test(this.name, { todo: this.todo }, function (tt) {
    this.test = tt
    tapStack.push(tt)
    suiteStack.push(this)
    var ret = this.fn()
    this.runAfter()
    suiteStack.pop()
    return ret
  }.bind(this))
}

Suite.prototype.runAfter = function () {
  this.after.forEach(function (namefn) {
    var name = namefn[0]
    var fn = namefn[1]
    before(name, fn)
  })
  do {
    var t = tapStack.pop()
  } while (t && t !== this.test)
  if (this.test && !this.test.results)
    t.end()
}

function before (name, fn) {
  if (typeof name === 'function')
    fn = name, name = null
  if (fn && fn.name && !name)
    name = fn.name
  var todo = !fn
  var suite = suiteStack[ suiteStack.length - 1 ]
  var t = tapStack[ tapStack.length - 1 ]
  if (!name)
    name = ''
  t.test(name, { todo: todo, silent: true }, function (tt) {
    var ret = fn.call(suite, done(tt))
    if (!ret && fn.length === 0)
      tt.end()
    else
      return ret
  })

  function done (tt) { return function (er) {
    if (er)
      tt.threw(er)
    else
      tt.end()
  }}
}

function it (name, fn) {
  if (typeof name === 'function')
    fn = name, name = null
  if (fn && fn.name && !name)
    name = fn.name
  var todo = !fn
  var suite = suiteStack[ suiteStack.length - 1 ]
  var t = tapStack[ tapStack.length - 1 ]
  if (!name)
    name = ''
  t.test(name, { todo: todo, tapMochaTest: true }, function (tt) {
    var ret = fn.call(tt, done(tt))
    if (ret && ret.then)
      return ret
    else if (fn.length === 0)
      tt.end()
  })

  function done (tt) { return function (er) {
    if (er)
      tt.threw(er)
    else
      tt.end()
  }}
}

function after (name, fn) {
  var suite = suiteStack[ suiteStack.length - 1 ]
  if (!suite)
    throw new Error('cannot call "after" outside of describe()')
  if (fn)
    suite.after.push([name, fn])
  else
    suite.after.push([name])
}

function moment (when, fn) {
  var t = tapStack[ tapStack.length - 1 ]
  t[when](function (cb) {
    if (!this.options.tapMochaTest)
      return cb()
    var suite = suiteStack[ suiteStack.length - 1 ]
    var ret = fn.call(this, cb)
    if (ret && ret.then)
      return ret
    else if (fn.length === 0)
      return cb()
  })
}

function beforeEach (fn) {
  moment('beforeEach', fn)
}

function afterEach (fn) {
  moment('afterEach', fn)
}
<|endoftext|>


<commit_before>/*
 * in
 * https://github.com/goliatone/in
 *
 * Copyright (c) 2015 goliatone
 * Licensed under the MIT license.
 */
/*jshint esversion:6*/
'use strict';

const extend = require('gextend');
const join = require('path').join;
const readdir = require('fs').readdir;
const separator = require('path').sep;
const extname = require('path').extname;
const resolve = require('path').resolve;
const exists = require('fs').existsSync;
const isAbsolute = require('path').isAbsolute;
const VError = require('verror');

const EventEmitter = require('events');
const multimatch = require('multimatch');

const _normalize = require('./normalizeArguments');
const _isDevelopment = require('./isDevelopment');
const _filterEmptyDir = require('./filter-empty-dir');

var DEFAULTS = {
    logger: console,
    autoinitialize: true,
    basepath: process.cwd(),
    normalize: _normalize,
    afterMount: function(){},
    mountHandler: function _mount(bean, context, config={}){
        var plugin = bean.plugin;
        if(typeof bean.config === 'function') return bean.config(plugin, context, config);
        config = extend({}, bean.config, config);
        if(typeof config.mount === 'function') return config.mount(plugin, context, config);
        if(typeof plugin.init === 'function') return plugin.init(context, config);
        return context[bean.id] = plugin;
    },
    sortFilter: function _sortFilter(plugins){
        function filter(a, b) {
            function p(i){
                return i.plugin.priority === undefined ? 0 : i.plugin.priority;
            }
            return p(a) < p(b) ? -1 : 1;
        }
        return plugins.sort(filter);
    }
};

class PluginLoader {
    constructor(config){
        config = extend({}, DEFAULTS, config);
        if(config.autoinitialize) this.init(config);
    }

    init(options = {}){

        this.context = new EventEmitter();

        extend(this, options);
    }

    isPluginId(plugin){
        return plugin.indexOf(separator) === -1;
    }

    normalizePath(path, basepath=this.basepath){
        return resolve(join(basepath, path));
    }
    /**
     *  Public: Scans `target` for files
     *  * `target` {String} Path to scan
     *  Returns Array {Array} Array containing absolute paths to all files in target
     */
    find(target) {
        if(!this.isPluginId(target) && !isAbsolute(target)){
            target = this.normalizePath(target, this.basepath);
        }

        if(!exists(target)) {
            this.logger.warn('find failed due to ENOENT: %s', target);
            return resolve([]);
        }

        return new Promise((resolve, reject)=>{
            readdir(target,(err, list)=>{
                if(err) return reject(err);

                list = list.map((f) => join(target, f));

                list = _filterEmptyDir(list);

                resolve(list);
            });
        });
    }

    /**
     *  Public: Apply `minimatch` patterns against `paths`,
     *  an array of paths.
     *  The default pattern is `['**', '!node_modules', '!.git']`
     *  * `paths` {Array} Array of paths
     *  * `patterns=['**', '!node_modules', '!.git']` {Array} `minimatch` patterns
     *
     * @argument {Array} paths Array of paths.
     * @argument {Array} patterns minimatch patterns
     * @return {Promise} Array containing filtered paths.
     */
    filter(paths, patterns = ['**', '!node_modules', '!.git', '!.json']){
        return Promise.resolve(multimatch(paths, patterns));
    }

    /**
     * exclude files that match a given pattern.
     *
     * @param  {Array} paths         List of paths
     * @param  {Array}  [patterns=[]] List of patterns
     * @return {Promise}
     */
    exclude(paths, patterns = []){

        if(!patterns || patterns.length === 0){
            return Promise.resolve(paths);
        }

        if(typeof patterns === 'string') {
            patterns = [patterns];
        }

        /*
         * If we have a negative pattern
         * we need to ensure the first pattern
         * is a `**` match all.
         * Check for a pattern with a !
         * If found, then ensuer index 0 is **
         */
        // if(!patterns.includes('**')) {
        //     patterns.unshift('**');
        // }

        //TODO: ensure we only have it at the start?

        let matched = [];
        let excluded = multimatch(paths, patterns);

        paths.forEach((p)=>{
            if(excluded.includes(p))return;
            matched.push(p);
        });

        return Promise.resolve(matched);
    }

    /**
     * Collect all plugins into an array.
     *
     * @type {Object}
     */
    load(plugins=[], options={}){
        options = extend({}, DEFAULTS, options);

        if(plugins.length === 0){
            this.logger.warn('PluginLoader: no plugins provided');
        }

        let basepath = options.basepath || this.basepath;

        if(options.plugins) {
            plugins = plugins.concat(options.plugins);
        }

        plugins = this.normalize(plugins, this.basepath);

        this._paths = plugins;
        this._plugins = [];

        return new Promise((resolve, reject)=>{
            plugins.map((bean) => {
                if(!this.isValidBeanExtension(bean.path)){
                    return this.logger.warn('Provided bean has invalid extension', bean.path);
                }

                bean.isLocal = true;
                try {
                    console.log('bean path', bean.path);
                    if(exists(bean.path)) {
                        bean.plugin = require(bean.path);
                    }
                    else if(exists(bean.path + '.js')) {
                        bean.plugin = require(bean.path + '.js');
                    } else {
                        bean.plugin = require(join(this.basepath, 'node_modules', bean.path));
                        bean.isLocal = false;
                    }
                } catch(e) {
                    let msg = 'Failed to require plugin "' + bean.id + '": ' + e.message;
                    this.logger.error(msg);
                    if(_isDevelopment()) {
                        this.logger.error(e.stack);
                    }
                    reject(new VError(e, msg));
                }
                //TODO: we could have a post require hook, to add meta to each plugin?
                // bean.priority = bean.plugin.priority === undefined ? 0 : bean.plugin.priority;
                // bean.dependencies = bean.plugin.dependencies === undefined ? [] : bean.plugin.dependencies;

                this._plugins.push(bean);
            });

            this.sort(this._plugins);

            resolve(this._plugins);
        });
    }

    /**
     * Applies `sortFilter` to the passed in
     * list of plugins.
     * You can set `sortFilter` using the config
     * object.
     * The deafult value will sort plugins on a
     * `priority` property.
     *
     * ```js
     * function _sortFilter(a, b) {
     *     function p(i){
     *         return i.priority === undefined ? 0 : i.priority;
     *     }
     *     return p(a) < p(b) ? -1 : 1;
     * }
     * ```
     *
     * Syncronous.
     * @param  {Array} plugins List of plugins.
     * @return {Array} List of ordered plugins.
     */
    sort(plugins){
        return this.sortFilter(plugins);
    }

    /**
     * Provided a list of loaded plugins it
     * will mount them in the provided `context`.
     *
     * It uses `mountHandler` which will do the
     * actual work.
     *
     * You can override `mountHandler` using the
     * config object.
     *
     * @param {Array} plugin List of loaded plugins.
     * @param {Object} config Configuration object.
     * @param {Object} context Passed as argument to
     *                         `mountHandler`
     * @type {Object}
     */
    mount(plugin, config={}, context=this.context){

        config = extend({}, {
            mountHandler: this.mountHandler,
            afterMount: this.afterMount
        }, config);

        let plugins = plugin;

        let {mountHandler} = config;

        if(!Array.isArray(plugins)) plugins = [plugin];

        return new Promise((resolve, reject)=>{
            try {
                plugins.map((plugin) => mountHandler(plugin, context, config));
            } catch(e) {
                if(_isDevelopment()) {
                    this.logger.error(e);
                }
                reject(new VError(e, 'Error running plugin: ' + plugin.id + '\n\n' + e.message+ '\n\n' + e.stack));
            }

            config.afterMount(context, config);

            resolve(context);
        });
    }

    /**
     * Mounts a directory containing plugin defintion
     * files.
     *
     * It runs the following operations:
     *
     * - find
     * - filter
     * - exclude
     * - load
     * - mount
     *
     * If `options.exclude` is an array with
     * `multimatch` patterns of files to exclude.
     *
     * @param  {String} directory              Path to directory
     * @param  {Object} [options={}]
     * @param  {Object} [context=this.context]
     * @return {Promise}
     */
    mountDirectory(directory, options= {}, context=this.context){
        return this.find(directory)
            .then(this.filter)
            .then((plugins) => this.exclude(plugins, options.exclude))
            .then((plugins) => this.load(plugins, options))
            .then((plugins) => this.mount(plugins, options, context));
    }

    mountList(list, options= {}, context=this.context){
        return this.load(list, options)
            .then((plugins) => this.mount(plugins, options, context));
    }

    isValidBeanExtension(path=''){
        let ext = extname(path);
        /*
         * Allow files without extension
         */
        if(!ext) return true;

        /*
         * If file has extension only
         * allow js files.
         */
        return ext === '.js';
    }
}

PluginLoader.DEFAULTS = DEFAULTS;

PluginLoader.normalize = _normalize;

/**
 * Expose PluginLoader
 */
module.exports = PluginLoader;
<commit_msg>remove console.log<commit_after>/*
 * in
 * https://github.com/goliatone/in
 *
 * Copyright (c) 2015 goliatone
 * Licensed under the MIT license.
 */
/*jshint esversion:6*/
'use strict';

const extend = require('gextend');
const join = require('path').join;
const readdir = require('fs').readdir;
const separator = require('path').sep;
const extname = require('path').extname;
const resolve = require('path').resolve;
const exists = require('fs').existsSync;
const isAbsolute = require('path').isAbsolute;
const VError = require('verror');

const EventEmitter = require('events');
const multimatch = require('multimatch');

const _normalize = require('./normalizeArguments');
const _isDevelopment = require('./isDevelopment');
const _filterEmptyDir = require('./filter-empty-dir');

var DEFAULTS = {
    logger: console,
    autoinitialize: true,
    basepath: process.cwd(),
    normalize: _normalize,
    afterMount: function() {},
    mountHandler: function _mount(bean, context, config = {}) {
        var plugin = bean.plugin;
        if (typeof bean.config === 'function') return bean.config(plugin, context, config);
        config = extend({}, bean.config, config);
        if (typeof config.mount === 'function') return config.mount(plugin, context, config);
        if (typeof plugin.init === 'function') return plugin.init(context, config);
        return context[bean.id] = plugin;
    },
    sortFilter: function _sortFilter(plugins) {
        function filter(a, b) {
            function p(i) {
                return i.plugin.priority === undefined ? 0 : i.plugin.priority;
            }
            return p(a) < p(b) ? -1 : 1;
        }
        return plugins.sort(filter);
    }
};

class PluginLoader {
    constructor(config) {
        config = extend({}, DEFAULTS, config);
        if (config.autoinitialize) this.init(config);
    }

    init(options = {}) {

        this.context = new EventEmitter();

        extend(this, options);
    }

    isPluginId(plugin) {
        return plugin.indexOf(separator) === -1;
    }

    normalizePath(path, basepath = this.basepath) {
            return resolve(join(basepath, path));
        }
        /**
         *  Public: Scans `target` for files
         *  * `target` {String} Path to scan
         *  Returns Array {Array} Array containing absolute paths to all files in target
         */
    find(target) {
        if (!this.isPluginId(target) && !isAbsolute(target)) {
            target = this.normalizePath(target, this.basepath);
        }

        if (!exists(target)) {
            this.logger.warn('find failed due to ENOENT: %s', target);
            return resolve([]);
        }

        return new Promise((resolve, reject) => {
            readdir(target, (err, list) => {
                if (err) return reject(err);

                list = list.map((f) => join(target, f));

                list = _filterEmptyDir(list);

                resolve(list);
            });
        });
    }

    /**
     *  Public: Apply `minimatch` patterns against `paths`,
     *  an array of paths.
     *  The default pattern is `['**', '!node_modules', '!.git']`
     *  * `paths` {Array} Array of paths
     *  * `patterns=['**', '!node_modules', '!.git']` {Array} `minimatch` patterns
     *
     * @argument {Array} paths Array of paths.
     * @argument {Array} patterns minimatch patterns
     * @return {Promise} Array containing filtered paths.
     */
    filter(paths, patterns = ['**', '!node_modules', '!.git', '!.json']) {
        return Promise.resolve(multimatch(paths, patterns));
    }

    /**
     * exclude files that match a given pattern.
     *
     * @param  {Array} paths         List of paths
     * @param  {Array}  [patterns=[]] List of patterns
     * @return {Promise}
     */
    exclude(paths, patterns = []) {

        if (!patterns || patterns.length === 0) {
            return Promise.resolve(paths);
        }

        if (typeof patterns === 'string') {
            patterns = [patterns];
        }

        /*
         * If we have a negative pattern
         * we need to ensure the first pattern
         * is a `**` match all.
         * Check for a pattern with a !
         * If found, then ensuer index 0 is **
         */
        // if(!patterns.includes('**')) {
        //     patterns.unshift('**');
        // }

        //TODO: ensure we only have it at the start?

        let matched = [];
        let excluded = multimatch(paths, patterns);

        paths.forEach((p) => {
            if (excluded.includes(p)) return;
            matched.push(p);
        });

        return Promise.resolve(matched);
    }

    /**
     * Collect all plugins into an array.
     *
     * @type {Object}
     */
    load(plugins = [], options = {}) {
        options = extend({}, DEFAULTS, options);

        if (plugins.length === 0) {
            this.logger.warn('PluginLoader: no plugins provided');
        }

        let basepath = options.basepath || this.basepath;

        if (options.plugins) {
            plugins = plugins.concat(options.plugins);
        }

        plugins = this.normalize(plugins, this.basepath);

        this._paths = plugins;
        this._plugins = [];

        return new Promise((resolve, reject) => {
            plugins.map((bean) => {
                if (!this.isValidBeanExtension(bean.path)) {
                    return this.logger.warn('Provided bean has invalid extension', bean.path);
                }

                bean.isLocal = true;
                try {
                    if (exists(bean.path)) {
                        bean.plugin = require(bean.path);
                    } else if (exists(bean.path + '.js')) {
                        bean.plugin = require(bean.path + '.js');
                    } else {
                        bean.plugin = require(join(this.basepath, 'node_modules', bean.path));
                        bean.isLocal = false;
                    }
                } catch (e) {
                    let msg = 'Failed to require plugin "' + bean.id + '": ' + e.message;
                    this.logger.error(msg);
                    if (_isDevelopment()) {
                        this.logger.error(e.stack);
                    }
                    reject(new VError(e, msg));
                }
                //TODO: we could have a post require hook, to add meta to each plugin?
                // bean.priority = bean.plugin.priority === undefined ? 0 : bean.plugin.priority;
                // bean.dependencies = bean.plugin.dependencies === undefined ? [] : bean.plugin.dependencies;

                this._plugins.push(bean);
            });

            this.sort(this._plugins);

            resolve(this._plugins);
        });
    }

    /**
     * Applies `sortFilter` to the passed in
     * list of plugins.
     * You can set `sortFilter` using the config
     * object.
     * The deafult value will sort plugins on a
     * `priority` property.
     *
     * ```js
     * function _sortFilter(a, b) {
     *     function p(i){
     *         return i.priority === undefined ? 0 : i.priority;
     *     }
     *     return p(a) < p(b) ? -1 : 1;
     * }
     * ```
     *
     * Syncronous.
     * @param  {Array} plugins List of plugins.
     * @return {Array} List of ordered plugins.
     */
    sort(plugins) {
        return this.sortFilter(plugins);
    }

    /**
     * Provided a list of loaded plugins it
     * will mount them in the provided `context`.
     *
     * It uses `mountHandler` which will do the
     * actual work.
     *
     * You can override `mountHandler` using the
     * config object.
     *
     * @param {Array} plugin List of loaded plugins.
     * @param {Object} config Configuration object.
     * @param {Object} context Passed as argument to
     *                         `mountHandler`
     * @type {Object}
     */
    mount(plugin, config = {}, context = this.context) {

        config = extend({}, {
            mountHandler: this.mountHandler,
            afterMount: this.afterMount
        }, config);

        let plugins = plugin;

        let { mountHandler } = config;

        if (!Array.isArray(plugins)) plugins = [plugin];

        return new Promise((resolve, reject) => {
            try {
                plugins.map((plugin) => mountHandler(plugin, context, config));
            } catch (e) {
                if (_isDevelopment()) {
                    this.logger.error(e);
                }
                reject(new VError(e, 'Error running plugin: ' + plugin.id + '\n\n' + e.message + '\n\n' + e.stack));
            }

            config.afterMount(context, config);

            resolve(context);
        });
    }

    /**
     * Mounts a directory containing plugin defintion
     * files.
     *
     * It runs the following operations:
     *
     * - find
     * - filter
     * - exclude
     * - load
     * - mount
     *
     * If `options.exclude` is an array with
     * `multimatch` patterns of files to exclude.
     *
     * @param  {String} directory              Path to directory
     * @param  {Object} [options={}]
     * @param  {Object} [context=this.context]
     * @return {Promise}
     */
    mountDirectory(directory, options = {}, context = this.context) {
        return this.find(directory)
            .then(this.filter)
            .then((plugins) => this.exclude(plugins, options.exclude))
            .then((plugins) => this.load(plugins, options))
            .then((plugins) => this.mount(plugins, options, context));
    }

    mountList(list, options = {}, context = this.context) {
        return this.load(list, options)
            .then((plugins) => this.mount(plugins, options, context));
    }

    isValidBeanExtension(path = '') {
        let ext = extname(path);
        /*
         * Allow files without extension
         */
        if (!ext) return true;

        /*
         * If file has extension only
         * allow js files.
         */
        return ext === '.js';
    }
}

PluginLoader.DEFAULTS = DEFAULTS;

PluginLoader.normalize = _normalize;

/**
 * Expose PluginLoader
 */
module.exports = PluginLoader;<|endoftext|>