<commit_before>//
//   Copyright 2015 Ilkka Oksanen <iao@iki.fi>
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing,
//   software distributed under the License is distributed on an "AS
//   IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
//   express or implied.  See the License for the specific language
//   governing permissions and limitations under the License.
//

'use strict';

const redisModule = require('../lib/redis');
const socketIo = require('socket.io');
const uuid = require('uid2');
const requestController = require('./request');
const log = require('../lib/log');
const friendsService = require('../services/friends');
const sessionService = require('../services/session');
const User = require('../models/user');
const UserGId = require('../models/userGId');
const conf = require('../lib/conf');
const userIntroducer = require('../lib/userIntroducer');

const ioServers = [];
const clientSocketList = [];

exports.setup = function setup(server) {
    const io = socketIo(server);
    ioServers.push(io);

    io.on('connection', socket => {
        const session = {
            id: null,
            user: null,
            state: 'connected' // connected, authenticated, terminating, disconnected
        };

        let redisSubscribe = null;

        clientSocketList.push(socket);

        socket.on('init', async function init(data) {
            if (session.id) {
                socket.emit('terminate', {
                    code: 'MULTIPLE_INITS',
                    reason: 'INIT event can be send only once per socket.io connection.'
                });
                await end('Multiple inits.');
                return;
            }

            const secret = data.secret;
            const userGId = UserGId.create(data.userId);

            if (!userGId || !secret) {
                log.info('Invalid init socket.io message.');
                socket.emit('terminate', {
                    code: 'INVALID_INIT',
                    reason: 'Invalid init event.'
                });
                await end('Invalid init.');
                return;
            }

            const user = await User.fetch(userGId.id);
            const ts = Math.round(Date.now() / 1000);

            if (!(user && user.get('secretExpires') > ts &&
                user.get('secret') === secret && user.get('inUse'))) {
                log.info(user, 'Init message with incorrect or expired secret.');
                socket.emit('terminate', {
                    code: 'INVALID_SECRET',
                    reason: 'Invalid or expired secret.'
                });
                await end('Invalid secret.');
                return;
            }

            session.user = user;
            session.state = 'authenticated';
            session.id = uuid(15);

            const maxBacklogMsgs = checkBacklogParameterBounds(data.maxBacklogMsgs);
            const cachedUpto = isInteger(data.cachedUpto) ? data.cachedUpto : 0;

            socket.emit('initok', {
                sessionId: session.id,
                maxBacklogMsgs
            });

            log.info(user, `New session init: ${session.id}, client: ${data.clientName}`);
            log.info(user, `maxBacklogMsgs: ${maxBacklogMsgs}, cachedUpto: ${cachedUpto}`);

            redisSubscribe = redisModule.createClient();
            await redisSubscribe.subscribe(user.id, `${user.id}:${session.id}`);

            let processing = false;
            const queue = [];

            async function process(channel, message) {
                if (processing) {
                    queue.push(message);
                    return;
                }

                processing = true;

                const ntf = JSON.parse(message);

                await userIntroducer.scanAndIntroduce(user, ntf, session, socket);

                socket.emit('ntf', ntf);

                if (ntf.id !== 'MSG') {
                    log.info(user, `Emitted ${ntf.id} (sessionId: ${session.id}) ${message}`);
                }

                processing = false;

                if (queue.length > 0) {
                    process(null, queue.shift());
                }
            }

            redisSubscribe.on('message', (channel, message) => {
                if (session.state === 'authenticated') {
                    process(channel, message);
                }
            });

            await userIntroducer.introduce(user, userGId, session);
            await sessionService.init(user, session, maxBacklogMsgs, cachedUpto);
        });

        socket.on('req', async function req(data, cb) {
            if (session.state !== 'authenticated') {
                await end('Request arrived before init.');
                return;
            }

            let resp;

            try {
                resp = await requestController.process(session, data);
            } catch (e) {
                resp = { status: 'INTERNAL_ERROR', errorMsg: 'Internal error.' };
                log.warn(session.user, `Exception: ${e}, stack: ${e.stack.replace(/\n/g, ',')}`);
            }

            await userIntroducer.scanAndIntroduce(session.user, resp, session);

            if (cb) {
                cb(resp); // Send the response as Socket.io acknowledgment.
            }

            if (session.state !== 'authenticated') {
                await end('Request processing requested termination.');
            }
        });

        socket.on('disconnect', async function disconnect() {
            await end('Socket.io disconnect.');
        });

        async function end(reason) {
            if (session.state !== 'disconnected') {
                session.state = 'disconnected';

                clientSocketList.splice(clientSocketList.indexOf(socket), 1);

                socket.disconnect(true);

                if (redisSubscribe) {
                    await redisSubscribe.unsubscribe();
                    const sessions = await redisSubscribe.pubsub('NUMSUB', session.user.id);
                    await redisSubscribe.quit();

                    if (sessions[1] === 0) {
                        await friendsService.informStateChange(session.user, 'logout');
                    }
                }

                const sessionIdExplained = session.id || '<not assigned>';
                log.info(`Session ${sessionIdExplained} ended. Reason: ${reason}`);
            }
        }
    });
};

exports.shutdown = function shutdown() {
    for (const server of ioServers) {
        server.close();
    }

    terminateClientConnections();
};

function checkBacklogParameterBounds(value) {
    const minAllowedBacklog = conf.get('session:min_backlog');
    const maxAllowedBacklog = conf.get('session:max_backlog');

    if (!isInteger(value)) {
        return maxAllowedBacklog;
    } else if (value < minAllowedBacklog) {
        return minAllowedBacklog;
    } else if (value > maxAllowedBacklog) {
        return maxAllowedBacklog;
    }

    return value;
}

function terminateClientConnections() {
    log.info(`Closing all ${clientSocketList.length} socket.io connections`);

    for (const socket of clientSocketList) {
        socket.disconnect(true);
    }
}

function isInteger(x) {
    return (typeof x === 'number') && (x % 1 === 0);
}
<commit_msg>Guard user in trace<commit_after>//
//   Copyright 2015 Ilkka Oksanen <iao@iki.fi>
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing,
//   software distributed under the License is distributed on an "AS
//   IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
//   express or implied.  See the License for the specific language
//   governing permissions and limitations under the License.
//

'use strict';

const redisModule = require('../lib/redis');
const socketIo = require('socket.io');
const uuid = require('uid2');
const requestController = require('./request');
const log = require('../lib/log');
const friendsService = require('../services/friends');
const sessionService = require('../services/session');
const User = require('../models/user');
const UserGId = require('../models/userGId');
const conf = require('../lib/conf');
const userIntroducer = require('../lib/userIntroducer');

const ioServers = [];
const clientSocketList = [];

exports.setup = function setup(server) {
    const io = socketIo(server);
    ioServers.push(io);

    io.on('connection', socket => {
        const session = {
            id: null,
            user: null,
            state: 'connected' // connected, authenticated, terminating, disconnected
        };

        let redisSubscribe = null;

        clientSocketList.push(socket);

        socket.on('init', async function init(data) {
            if (session.id) {
                socket.emit('terminate', {
                    code: 'MULTIPLE_INITS',
                    reason: 'INIT event can be send only once per socket.io connection.'
                });
                await end('Multiple inits.');
                return;
            }

            const secret = data.secret;
            const userGId = UserGId.create(data.userId);

            if (!userGId || !secret) {
                log.info('Invalid init socket.io message.');
                socket.emit('terminate', {
                    code: 'INVALID_INIT',
                    reason: 'Invalid init event.'
                });
                await end('Invalid init.');
                return;
            }

            const user = await User.fetch(userGId.id);
            const ts = Math.round(Date.now() / 1000);

            if (!(user && user.get('secretExpires') > ts &&
                user.get('secret') === secret && user.get('inUse'))) {

                if (user) {
                    log.info(user, 'Init message with incorrect or expired secret.');
                }

                socket.emit('terminate', {
                    code: 'INVALID_SECRET',
                    reason: 'Invalid or expired secret.'
                });
                await end('Invalid secret.');
                return;
            }

            session.user = user;
            session.state = 'authenticated';
            session.id = uuid(15);

            const maxBacklogMsgs = checkBacklogParameterBounds(data.maxBacklogMsgs);
            const cachedUpto = isInteger(data.cachedUpto) ? data.cachedUpto : 0;

            socket.emit('initok', {
                sessionId: session.id,
                maxBacklogMsgs
            });

            log.info(user, `New session init: ${session.id}, client: ${data.clientName}`);
            log.info(user, `maxBacklogMsgs: ${maxBacklogMsgs}, cachedUpto: ${cachedUpto}`);

            redisSubscribe = redisModule.createClient();
            await redisSubscribe.subscribe(user.id, `${user.id}:${session.id}`);

            let processing = false;
            const queue = [];

            async function process(channel, message) {
                if (processing) {
                    queue.push(message);
                    return;
                }

                processing = true;

                const ntf = JSON.parse(message);

                await userIntroducer.scanAndIntroduce(user, ntf, session, socket);

                socket.emit('ntf', ntf);

                if (ntf.id !== 'MSG') {
                    log.info(user, `Emitted ${ntf.id} (sessionId: ${session.id}) ${message}`);
                }

                processing = false;

                if (queue.length > 0) {
                    process(null, queue.shift());
                }
            }

            redisSubscribe.on('message', (channel, message) => {
                if (session.state === 'authenticated') {
                    process(channel, message);
                }
            });

            await userIntroducer.introduce(user, userGId, session);
            await sessionService.init(user, session, maxBacklogMsgs, cachedUpto);
        });

        socket.on('req', async function req(data, cb) {
            if (session.state !== 'authenticated') {
                await end('Request arrived before init.');
                return;
            }

            let resp;

            try {
                resp = await requestController.process(session, data);
            } catch (e) {
                resp = { status: 'INTERNAL_ERROR', errorMsg: 'Internal error.' };
                log.warn(session.user, `Exception: ${e}, stack: ${e.stack.replace(/\n/g, ',')}`);
            }

            await userIntroducer.scanAndIntroduce(session.user, resp, session);

            if (cb) {
                cb(resp); // Send the response as Socket.io acknowledgment.
            }

            if (session.state !== 'authenticated') {
                await end('Request processing requested termination.');
            }
        });

        socket.on('disconnect', async function disconnect() {
            await end('Socket.io disconnect.');
        });

        async function end(reason) {
            if (session.state !== 'disconnected') {
                session.state = 'disconnected';

                clientSocketList.splice(clientSocketList.indexOf(socket), 1);

                socket.disconnect(true);

                if (redisSubscribe) {
                    await redisSubscribe.unsubscribe();
                    const sessions = await redisSubscribe.pubsub('NUMSUB', session.user.id);
                    await redisSubscribe.quit();

                    if (sessions[1] === 0) {
                        await friendsService.informStateChange(session.user, 'logout');
                    }
                }

                const sessionIdExplained = session.id || '<not assigned>';
                log.info(`Session ${sessionIdExplained} ended. Reason: ${reason}`);
            }
        }
    });
};

exports.shutdown = function shutdown() {
    for (const server of ioServers) {
        server.close();
    }

    terminateClientConnections();
};

function checkBacklogParameterBounds(value) {
    const minAllowedBacklog = conf.get('session:min_backlog');
    const maxAllowedBacklog = conf.get('session:max_backlog');

    if (!isInteger(value)) {
        return maxAllowedBacklog;
    } else if (value < minAllowedBacklog) {
        return minAllowedBacklog;
    } else if (value > maxAllowedBacklog) {
        return maxAllowedBacklog;
    }

    return value;
}

function terminateClientConnections() {
    log.info(`Closing all ${clientSocketList.length} socket.io connections`);

    for (const socket of clientSocketList) {
        socket.disconnect(true);
    }
}

function isInteger(x) {
    return (typeof x === 'number') && (x % 1 === 0);
}
<|endoftext|>


<commit_before>// TODO fold these into jx.js?
function loadJS(url) {
  var scriptElement = document.createElement('script')
  scriptElement.setAttribute("type","text/javascript")
  scriptElement.setAttribute("src", url)
  document.getElementsByTagName("head")[0].appendChild(scriptElement)

  return scriptElement;
}

function loadCSS(url) {
  var linkElement = document.createElement("link")
  linkElement.setAttribute("rel", "stylesheet")
  linkElement.setAttribute("type", "text/css")
  linkElement.setAttribute("href", url)
  document.getElementsByTagName("head")[0].appendChild(linkElement)

  return linkElement;
}

var Qlb = {};

Qlb.Environment = function(table, outer) {
  this.table = table
  this.outer = outer
  this.find = function (sym) { return this.table[sym] === undefined ? (this.outer ? this.outer.find(sym) : undefined) : this.table[sym] }
  this.merge = function(other) { for(var name in other) { Qlb.symbols[name] = true; this.table[name] = other[name] } }
}

Qlb.globalEnvironment = new Qlb.Environment({});

Qlb.symbols = {
  "حرفي": true,
  "إفعل": true,
  "إذا": true,
  "حدد": true,
  "لامدا": true
}

Qlb.isSymbol = function(sym) {
  return !!Qlb.symbols[sym];
}

Qlb.init = function(onloaded) {
  // Load grammar from separate file, because dealing with JavaScripts lack of
  // multiline strings AND Arabic input is just too much. Just too much.
  jx.load("/peg/qlb.peg", function(grammar) {
    // Load grammar
    Qlb.parser = PEG.buildParser(grammar);

    // Default console is window consle
    if(Qlb.console === 'undefined') Qlb.console = window.console;

    Qlb.eval = function(exp, env) {
      if(typeof exp == "string") {            // evaling string/symbol
        var sym = env.find(exp);
        if(sym === undefined) return exp;
        else return sym;

      } else if(!(exp instanceof Array)) {    // evaling literal
        return exp;

      } else {                                // evaling list
        var first = exp[0]
        var rest = exp.slice(1)

        if(first == "حرفي") {
          return rest

        } else if(first == "إفعل") {
          rest = rest.map(function(e) { return Qlb.eval(e, env) });
          return rest[rest.length - 1];

        } else if(first == "إذا") {
          var test = rest[0],
              ifex = rest[1],
              elex = rest[2]

          return Qlb.eval(Qlb.eval(test, env) ? ifex : elex, env)

        } else if(first == "حدد") {
          var sym = rest[0],
              val = rest[1]
          return (env.table[sym] = Qlb.eval(val, env))

        } else if(first == "لامدا") {
          var params = rest[0],
              body = rest.slice(1)

          return function() {
            var table = {}
            for (var i = 0; i < params.length; i++)
              table[params[i]] = arguments[i]

            return Qlb.eval(body, new Qlb.Environment(table, env))
          }

        } else {
          var exps = exp.map(function(p) { return Qlb.eval(p, env) })
          if(typeof exps[0] == "function")
            // first element evaluates to a function
            return exps.shift().apply(this, exps)

          else if(Qlb.isSymbol(exps[0])) {
            throw new ReferenceError(exps[0]);

          } else
            // return last element
            return exps[exps.length - 1];

        }
      }
    }

    Qlb.execute = function(code) {
      try {
        var ast = Qlb.parser.parse(code);
        var val;
        for (var i = 0; i < ast.length; i++) {
          val = Qlb.eval(ast[i], Qlb.globalEnvironment);
        };
        return val;

      } catch(e) {
        Qlb.handleUncaughtException(e);

      }
    }

    Qlb.handleUncaughtException = function(e) {
      switch(e.name) {
        case "SyntaxError":
          Qlb.console.warn("خطأ: حرف '" + e.found + "' غير متوقع");
          break;

        case "ReferenceError":
          Qlb.console.warn("خطأ: رمز '" + e.message + "' غير موجود");
          break;

        default:
          throw e;

      }
    }

    if(onloaded) onloaded();
  });
}

<commit_msg>Add عدل special form<commit_after>// TODO fold these into jx.js?
function loadJS(url) {
  var scriptElement = document.createElement('script')
  scriptElement.setAttribute("type","text/javascript")
  scriptElement.setAttribute("src", url)
  document.getElementsByTagName("head")[0].appendChild(scriptElement)

  return scriptElement;
}

function loadCSS(url) {
  var linkElement = document.createElement("link")
  linkElement.setAttribute("rel", "stylesheet")
  linkElement.setAttribute("type", "text/css")
  linkElement.setAttribute("href", url)
  document.getElementsByTagName("head")[0].appendChild(linkElement)

  return linkElement;
}

var Qlb = {};

Qlb.Environment = function(table, outer) {
  this.table = table
  this.outer = outer
  this.find = function (sym) { return this.table[sym] === undefined ? (this.outer ? this.outer.find(sym) : undefined) : this.table[sym] }
  this.envForSym = function (sym) { return this.table[sym] === undefined ? (this.outer ? this.outer : undefined) : this }
  this.merge = function(other) { for(var name in other) { Qlb.symbols[name] = true; this.table[name] = other[name] } }
}

Qlb.globalEnvironment = new Qlb.Environment({});

Qlb.symbols = {
  "حرفي": true,
  "إفعل": true,
  "إذا": true,
  "حدد": true,
  "لامدا": true
}

Qlb.isSymbol = function(sym) {
  return !!Qlb.symbols[sym];
}

Qlb.init = function(onloaded) {
  // Load grammar from separate file, because dealing with JavaScripts lack of
  // multiline strings AND Arabic input is just too much. Just too much.
  jx.load("/peg/qlb.peg", function(grammar) {
    // Load grammar
    Qlb.parser = PEG.buildParser(grammar);

    // Default console is window consle
    if(Qlb.console === 'undefined') Qlb.console = window.console;

    Qlb.eval = function(exp, env) {
      if(typeof exp == "string") {            // evaling string/symbol
        var sym = env.find(exp);
        if(sym === undefined) return exp;
        else return sym;

      } else if(!(exp instanceof Array)) {    // evaling literal
        return exp;

      } else {                                // evaling list
        var first = exp[0]
        var rest = exp.slice(1)

        if(first == "حرفي") {
          return rest

        } else if(first == "إفعل") {
          rest = rest.map(function(e) { return Qlb.eval(e, env) });
          return rest[rest.length - 1];

        } else if(first == "إذا") {
          var test = rest[0],
              ifex = rest[1],
              elex = rest[2]

          return Qlb.eval(Qlb.eval(test, env) ? ifex : elex, env)

        } else if(first == "حدد") {
          var sym = rest[0],
              val = rest[1]
          return (env.table[sym] = Qlb.eval(val, env))

        } else if(first == "عدل") {
          var sym = rest[0],
              val = rest[1]

          if(env.envForSym(sym))
            return (env.envForSym(sym).table[sym] = Qlb.eval(val, env));
          return undefined;

        } else if(first == "لامدا") {
          var params = rest[0],
              body = rest.slice(1)

          return function() {
            var table = {}
            for (var i = 0; i < params.length; i++)
              table[params[i]] = arguments[i]

            return Qlb.eval(body, new Qlb.Environment(table, env))
          }

        } else {
          var exps = exp.map(function(p) { return Qlb.eval(p, env) })
          if(typeof exps[0] == "function")
            // first element evaluates to a function
            return exps.shift().apply(this, exps)

          else if(Qlb.isSymbol(exps[0])) {
            throw new ReferenceError(exps[0]);

          } else
            // return last element
            return exps[exps.length - 1];

        }
      }
    }

    Qlb.execute = function(code) {
      try {
        var ast = Qlb.parser.parse(code);
        var val;
        for (var i = 0; i < ast.length; i++) {
          val = Qlb.eval(ast[i], Qlb.globalEnvironment);
        };
        return val;

      } catch(e) {
        Qlb.handleUncaughtException(e);

      }
    }

    Qlb.handleUncaughtException = function(e) {
      switch(e.name) {
        case "SyntaxError":
          Qlb.console.warn("خطأ: حرف '" + e.found + "' غير متوقع");
          break;

        case "ReferenceError":
          Qlb.console.warn("خطأ: رمز '" + e.message + "' غير موجود");
          break;

        default:
          throw e;

      }
    }

    if(onloaded) onloaded();
  });
}

<|endoftext|>


<commit_before>import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { render } from '@ember/test-helpers';
import hbs from 'htmlbars-inline-precompile';

module('select-box/option', function(hooks) {
  setupRenderingTest(hooks);

  test('it renders', async function(assert) {
    assert.expect(1);

    await render(hbs `{{select-box/option}}`);

    assert.equal(this.$('div.select-box-option').length, 1,
      'renders with correct class name and tag');
  });

  test('class prefix', async function(assert) {
    assert.expect(1);

    await render(hbs `{{select-box/option class-prefix="foo"}}`);

    assert.equal(this.$('.foo-option').length, 1,
      'can override the class prefix');
  });

  test('aria role', async function(assert) {
    assert.expect(1);

    await render(hbs `{{select-box/option}}`);

    assert.equal(this.$('.select-box-option').attr('role'), 'option',
      'a select box option has an appropriate aria role');
  });

  test('title', async function(assert) {
    assert.expect(1);

    await render(hbs `{{select-box/option title="Foo"}}`);

    assert.equal(this.$('.select-box-option').attr('title'), 'Foo',
      'a select box option can have a title attribute');
  });

  test('style', async function(assert) {
    assert.expect(1);

    await render(hbs `{{select-box/option style="color:red<script>"}}`);

    assert.ok(this.$().html().match('style="color:red&lt;script&gt;"'),
      'an option can be styled, value is escaped');
  });

  test('disabled', async function(assert) {
    assert.expect(1);

    await render(hbs `{{select-box/option disabled=true}}`);

    assert.ok(this.$('.select-box-option[aria-disabled]').hasClass('is-disabled'),
      'an option can be flagged as disabled');
  });

  test('aria selected', async function(assert) {
    assert.expect(2);

    this.set('value', 1);

    await render(hbs `
      {{#select-box value=value as |sb|}}
        {{sb.option value=1 label="One"}}
        {{sb.option value=2 label="Two"}}
      {{/select-box}}
    `);

    assert.equal(this.$('.select-box-option[aria-selected]').text(), 'One',
      'the selected option receives an aria selected attribute');

    this.set('value', 2);

    assert.equal(this.$('.select-box-option[aria-selected]').text(), 'Two',
      'the aria selected attribute is redetermined when the value changes');
  });

  test('yield index', async function(assert) {
    assert.expect(2);

    const foo = { myValue: 'foo', myLabel: 'Foo' };
    const bar = { myValue: 'bar', myLabel: 'Bar' };
    const baz = { myValue: 'baz', myLabel: 'Baz' };
    const qux = { myValue: 'qux', myLabel: 'Qux' };

    this.set('group1', [foo, bar]);
    this.set('group2', [baz, qux]);

    await render(hbs`
      {{#select-box value="baz" as |sb|}}
        {{#sb.group label="Group 1"}}
          {{#each group1 as |item i|}}
            {{#sb.option value=item.myValue label=item.myLabel as |o|~}}
              {{o.label}}={{o.value}} {{i}} ({{o.index}}) {{o.selected}}
            {{~/sb.option}}
          {{/each}}
        {{/sb.group}}
        {{#sb.group label="Group 2"}}
          {{#each group2 as |item i|}}
            {{#sb.option value=item.myValue label=item.myLabel as |o|~}}
              {{o.label}}={{o.value}} {{i}} ({{o.index}}) {{o.selected}}
            {{~/sb.option}}
          {{/each}}
        {{/sb.group}}
      {{/select-box}}
    `);

    assert.ok(
      this.$('.select-box-option:eq(0)').text() === 'Foo=foo 0 (0) false' &&
      this.$('.select-box-option:eq(1)').text() === 'Bar=bar 1 (1) false' &&
      this.$('.select-box-option:eq(2)').text() === 'Baz=baz 0 (2) true' &&
      this.$('.select-box-option:eq(3)').text() === 'Qux=qux 1 (3) false',
      'select box options can yield their label, value, index and selected state'
    );

    this.set('group2', [qux, baz]);

    assert.ok(
      this.$('.select-box-option:eq(0)').text() === 'Foo=foo 0 (0) false' &&
      this.$('.select-box-option:eq(1)').text() === 'Bar=bar 1 (1) false' &&
      this.$('.select-box-option:eq(2)').text() === 'Qux=qux 0 (3) false' &&
      this.$('.select-box-option:eq(3)').text() === 'Baz=baz 1 (2) true',
      'index gets out of sync due to lack of key="@index"'
    );
  });

  test('yield disabled', async function(assert) {
    assert.expect(2);

    this.set('fooDisabled', true);

    await render(hbs`
      {{#select-box as |sb|}}
        {{#sb.option disabled=fooDisabled as |o|}}
          foo {{if o.disabled "disabled"}}
        {{/sb.option}}
      {{/select-box}}
    `);

    assert.equal(this.$('.select-box-option').text().trim(), 'foo disabled',
      'yields disabled state');

    this.set('fooDisabled', false);

    assert.equal(this.$('.select-box-option').text().trim(), 'foo',
      'disabled state is updated');
  });
});
<commit_msg>add test<commit_after>import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { render } from '@ember/test-helpers';
import hbs from 'htmlbars-inline-precompile';

module('select-box/option', function(hooks) {
  setupRenderingTest(hooks);

  test('it renders', async function(assert) {
    assert.expect(1);

    await render(hbs `{{select-box/option}}`);

    assert.equal(this.$('div.select-box-option').length, 1,
      'renders with correct class name and tag');
  });

  test('class prefix', async function(assert) {
    assert.expect(1);

    await render(hbs `{{select-box/option class-prefix="foo"}}`);

    assert.equal(this.$('.foo-option').length, 1,
      'can override the class prefix');
  });

  test('aria role', async function(assert) {
    assert.expect(1);

    await render(hbs `{{select-box/option}}`);

    assert.equal(this.$('.select-box-option').attr('role'), 'option',
      'a select box option has an appropriate aria role');
  });

  test('title', async function(assert) {
    assert.expect(1);

    await render(hbs `{{select-box/option title="Foo"}}`);

    assert.equal(this.$('.select-box-option').attr('title'), 'Foo',
      'a select box option can have a title attribute');
  });

  test('style', async function(assert) {
    assert.expect(1);

    await render(hbs `{{select-box/option style="color:red<script>"}}`);

    assert.ok(this.$().html().match('style="color:red&lt;script&gt;"'),
      'an option can be styled, value is escaped');
  });

  test('disabled', async function(assert) {
    assert.expect(1);

    await render(hbs `{{select-box/option disabled=true}}`);

    assert.ok(this.$('.select-box-option[aria-disabled]').hasClass('is-disabled'),
      'an option can be flagged as disabled');
  });

  test('aria selected', async function(assert) {
    assert.expect(2);

    this.set('value', 1);

    await render(hbs `
      {{#select-box value=value as |sb|}}
        {{sb.option value=1 label="One"}}
        {{sb.option value=2 label="Two"}}
      {{/select-box}}
    `);

    assert.equal(this.$('.select-box-option[aria-selected]').text(), 'One',
      'the selected option receives an aria selected attribute');

    this.set('value', 2);

    assert.equal(this.$('.select-box-option[aria-selected]').text(), 'Two',
      'the aria selected attribute is redetermined when the value changes');
  });

  test('yield index', async function(assert) {
    assert.expect(2);

    const foo = { myValue: 'foo', myLabel: 'Foo' };
    const bar = { myValue: 'bar', myLabel: 'Bar' };
    const baz = { myValue: 'baz', myLabel: 'Baz' };
    const qux = { myValue: 'qux', myLabel: 'Qux' };

    this.set('group1', [foo, bar]);
    this.set('group2', [baz, qux]);

    await render(hbs`
      {{#select-box value="baz" as |sb|}}
        {{#sb.group label="Group 1"}}
          {{#each group1 as |item i|}}
            {{#sb.option value=item.myValue label=item.myLabel as |o|~}}
              {{o.label}}={{o.value}} {{i}} ({{o.index}}) {{o.selected}}
            {{~/sb.option}}
          {{/each}}
        {{/sb.group}}
        {{#sb.group label="Group 2"}}
          {{#each group2 as |item i|}}
            {{#sb.option value=item.myValue label=item.myLabel as |o|~}}
              {{o.label}}={{o.value}} {{i}} ({{o.index}}) {{o.selected}}
            {{~/sb.option}}
          {{/each}}
        {{/sb.group}}
      {{/select-box}}
    `);

    assert.ok(
      this.$('.select-box-option:eq(0)').text() === 'Foo=foo 0 (0) false' &&
      this.$('.select-box-option:eq(1)').text() === 'Bar=bar 1 (1) false' &&
      this.$('.select-box-option:eq(2)').text() === 'Baz=baz 0 (2) true' &&
      this.$('.select-box-option:eq(3)').text() === 'Qux=qux 1 (3) false',
      'select box options can yield their label, value, index and selected state'
    );

    this.set('group2', [qux, baz]);

    assert.ok(
      this.$('.select-box-option:eq(0)').text() === 'Foo=foo 0 (0) false' &&
      this.$('.select-box-option:eq(1)').text() === 'Bar=bar 1 (1) false' &&
      this.$('.select-box-option:eq(2)').text() === 'Qux=qux 0 (3) false' &&
      this.$('.select-box-option:eq(3)').text() === 'Baz=baz 1 (2) true',
      'index gets out of sync due to lack of key="@index"'
    );
  });

  test('yield index', async function(assert) {
    assert.expect(4);

    const labels = () => {
      return this.$('.select-box-option')
        .map((i, o) => o.textContent.trim())
        .toArray();
    };

    this.set('values', ['foo', 'bar', 'baz']);

    await render(hbs`
      {{#select-box value="baz" as |sb|}}
        {{#each values as |value|}}
          {{#sb.option value=value as |o|~}}
            {{value}}: {{o.index}}
          {{~/sb.option}}
        {{/each}}
      {{/select-box}}
    `);

    assert.deepEqual(labels(), ['foo: 0', 'bar: 1', 'baz: 2'],
      'precondition, indexes are correct');

    this.set('values', ['foo', 'baz']);
    this.set('values', ['foo', 'bar', 'baz']);

    assert.deepEqual(labels(), ['foo: 0', 'bar: 2', 'baz: 1'],
      'indexes are wrong due to component re-use');

    await render(hbs`
      {{#select-box value="baz" as |sb|}}
        {{#each values key="@index" as |value|}}
          {{#sb.option value=value as |o|~}}
            {{value}}: {{o.index}}
          {{~/sb.option}}
        {{/each}}
      {{/select-box}}
    `);

    assert.deepEqual(labels(), ['foo: 0', 'bar: 1', 'baz: 2'],
      'precondition, indexes are correct');

    this.set('values', ['foo', 'baz']);
    this.set('values', ['foo', 'bar', 'baz']);

    assert.deepEqual(labels(), ['foo: 0', 'bar: 1', 'baz: 2'],
      'indexes are correct due to key on each');
  });

  test('yield disabled', async function(assert) {
    assert.expect(2);

    this.set('fooDisabled', true);

    await render(hbs`
      {{#select-box as |sb|}}
        {{#sb.option disabled=fooDisabled as |o|}}
          foo {{if o.disabled "disabled"}}
        {{/sb.option}}
      {{/select-box}}
    `);

    assert.equal(this.$('.select-box-option').text().trim(), 'foo disabled',
      'yields disabled state');

    this.set('fooDisabled', false);

    assert.equal(this.$('.select-box-option').text().trim(), 'foo',
      'disabled state is updated');
  });
});
<|endoftext|>


<commit_before>const logLevelHierarchy = {
	'debug' : 3,
	'warn'  : 2,
	'error' : 1,
	'off'   : 0
}

module.export = logLevelHierarchy;
<commit_msg>removing unneeded files.<commit_after><|endoftext|>


<commit_before>import Carousel from '../src/index';
import React from 'react';
import ReactDom from 'react-dom';

const colors = ['7732bb', '047cc0', '00884b', 'e3bc13', 'db7c00', 'aa231f'];

class App extends React.Component {
  constructor() {
    super(...arguments);
    this.state = {
      slideIndex: 0,
      length: 6,
      wrapAround: false,
      animation: undefined,
      underlineHeader: false,
      zoomScale: 0.5,
      slidesToShow: 1,
      cellAlign: 'left',
      transitionMode: 'scroll',
      heightMode: 'max',
      withoutControls: false
    };

    this.handleImageClick = this.handleImageClick.bind(this);
    this.handleZoomChangeChange = this.handleZoomChangeChange.bind(this);
  }

  handleImageClick() {
    this.setState({ underlineHeader: !this.state.underlineHeader });
  }

  handleZoomChangeChange(event) {
    this.setState({
      zoomScale: event.target.value
    });
  }

  render() {
    return (

        <Carousel
          withoutControls={this.state.withoutControls}
          transitionMode={this.state.transitionMode}
          cellAlign={this.state.cellAlign}
          animation={this.state.animation}
          zoomScale={Number(this.state.zoomScale || 0)}
          wrapAround={this.state.wrapAround}
          slideIndex={this.state.slideIndex}
          heightMode={this.state.heightMode}
          renderTopCenterControls={({ currentSlide }) => (

              Nuka Carousel: Slide {currentSlide + 1}

          )}
          renderAnnounceSlideMessage={({ currentSlide, slideCount }) => {
            return `Showing slide ${currentSlide + 1} of ${slideCount}`;
          }}
        >
          {colors.slice(0, this.state.length).map((color, index) => (
            <img
              src={`http://placehold.it/1000x400/${color}/ffffff/&text=slide${index +
                1}`}
              alt={`Slide ${index + 1}`}
              key={color}
              onClick={this.handleImageClick}
              style={{
                height:
                  this.state.heightMode === 'current' ? 100 * (index + 1) : 400
              }}
            />
          ))}
        </Carousel>

            <button onClick={() => this.setState({ slideIndex: 0 })}>1</button>
            <button onClick={() => this.setState({ slideIndex: 1 })}>2</button>
            <button onClick={() => this.setState({ slideIndex: 2 })}>3</button>
            <button onClick={() => this.setState({ slideIndex: 3 })}>4</button>
            <button onClick={() => this.setState({ slideIndex: 4 })}>5</button>
            <button onClick={() => this.setState({ slideIndex: 5 })}>6</button>

            <button
              onClick={() =>
                this.setState(prevState => {
                  const length = prevState.length === 6 ? 3 : 6;
                  return {
                    length
                  };
                })
              }
            >
              Toggle Show 3 Slides Only
            </button>
            <button
              onClick={() =>
                this.setState({
                  transitionMode:
                    this.state.transitionMode === 'fade' ? 'scroll' : 'fade',
                  animation: undefined
                })
              }
            >
              Toggle Fade {this.state.transitionMode === 'fade' ? 'Off' : 'On'}
            </button>
            <button
              onClick={() =>
                this.setState(prevState => ({
                  wrapAround: !prevState.wrapAround
                }))
              }
            >
              Toggle Wrap Around
            </button>

        {this.state.transitionMode !== 'fade' && (
          <>

              {this.state.slidesToShow > 1.0 && (

                  <button onClick={() => this.setState({ cellAlign: 'left' })}>
                    Left
                  </button>
                  <button
                    onClick={() => this.setState({ cellAlign: 'center' })}
                  >
                    Center
                  </button>
                  <button onClick={() => this.setState({ cellAlign: 'right' })}>
                    Right
                  </button>

              )}

                <button
                  onClick={() =>
                    this.setState({
                      slidesToShow: this.state.slidesToShow > 1.0 ? 1.0 : 1.25
                    })
                  }
                >
                  Toggle Partially Visible Slides
                </button>
                <button
                  onClick={() =>
                    this.setState({
                      heightMode:
                        this.state.heightMode === 'current' ? 'max' : 'current'
                    })
                  }
                >
                  Toggle Height Mode Current
                </button>
                <button
                  onClick={() =>
                    this.setState({
                      withoutControls: !this.state.withoutControls
                    })
                  }
                >
                  Toggle Controls
                </button>

              {this.state.animation === 'zoom' && (
                <input
                  type="number"
                  value={this.state.zoomScale}
                  onChange={this.handleZoomChangeChange}
                />
              )}
              <button
                onClick={() =>
                  this.setState({
                    animation:
                      this.state.animation === 'zoom' ? undefined : 'zoom',
                    cellAlign: 'center'
                  })
                }
              >
                Toggle Zoom Animation{' '}
                {this.state.animation === 'zoom' ? 'Off' : 'On'}
              </button>

          </>
        )}

    );
  }
}

ReactDom.render(<App />, document.getElementById('content'));
<commit_msg>fix typos<commit_after>import Carousel from '../src/index';
import React from 'react';
import ReactDom from 'react-dom';

const colors = ['7732bb', '047cc0', '00884b', 'e3bc13', 'db7c00', 'aa231f'];

class App extends React.Component {
  constructor() {
    super(...arguments);
    this.state = {
      slideIndex: 0,
      length: 6,
      wrapAround: false,
      animation: undefined,
      underlineHeader: false,
      zoomScale: 0.5,
      slidesToShow: 1,
      cellAlign: 'left',
      transitionMode: 'scroll',
      heightMode: 'max',
      withoutControls: false
    };

    this.handleImageClick = this.handleImageClick.bind(this);
    this.handleZoomScaleChange = this.handleZoomScaleChange.bind(this);
  }

  handleImageClick() {
    this.setState({ underlineHeader: !this.state.underlineHeader });
  }

  handleZoomScaleChange(event) {
    this.setState({
      zoomScale: event.target.value
    });
  }

  render() {
    return (

        <Carousel
          withoutControls={this.state.withoutControls}
          transitionMode={this.state.transitionMode}
          cellAlign={this.state.cellAlign}
          animation={this.state.animation}
          zoomScale={Number(this.state.zoomScale || 0)}
          wrapAround={this.state.wrapAround}
          slideIndex={this.state.slideIndex}
          heightMode={this.state.heightMode}
          renderTopCenterControls={({ currentSlide }) => (

              Nuka Carousel: Slide {currentSlide + 1}

          )}
          renderAnnounceSlideMessage={({ currentSlide, slideCount }) => {
            return `Showing slide ${currentSlide + 1} of ${slideCount}`;
          }}
        >
          {colors.slice(0, this.state.length).map((color, index) => (
            <img
              src={`http://placehold.it/1000x400/${color}/ffffff/&text=slide${index +
                1}`}
              alt={`Slide ${index + 1}`}
              key={color}
              onClick={this.handleImageClick}
              style={{
                height:
                  this.state.heightMode === 'current' ? 100 * (index + 1) : 400
              }}
            />
          ))}
        </Carousel>

            <button onClick={() => this.setState({ slideIndex: 0 })}>1</button>
            <button onClick={() => this.setState({ slideIndex: 1 })}>2</button>
            <button onClick={() => this.setState({ slideIndex: 2 })}>3</button>
            <button onClick={() => this.setState({ slideIndex: 3 })}>4</button>
            <button onClick={() => this.setState({ slideIndex: 4 })}>5</button>
            <button onClick={() => this.setState({ slideIndex: 5 })}>6</button>

            <button
              onClick={() =>
                this.setState(prevState => {
                  const length = prevState.length === 6 ? 3 : 6;
                  return {
                    length
                  };
                })
              }
            >
              Toggle Show 3 Slides Only
            </button>
            <button
              onClick={() =>
                this.setState({
                  transitionMode:
                    this.state.transitionMode === 'fade' ? 'scroll' : 'fade',
                  animation: undefined
                })
              }
            >
              Toggle Fade {this.state.transitionMode === 'fade' ? 'Off' : 'On'}
            </button>
            <button
              onClick={() =>
                this.setState(prevState => ({
                  wrapAround: !prevState.wrapAround
                }))
              }
            >
              Toggle Wrap Around
            </button>

        {this.state.transitionMode !== 'fade' && (
          <>

              {this.state.slidesToShow > 1.0 && (

                  <button onClick={() => this.setState({ cellAlign: 'left' })}>
                    Left
                  </button>
                  <button
                    onClick={() => this.setState({ cellAlign: 'center' })}
                  >
                    Center
                  </button>
                  <button onClick={() => this.setState({ cellAlign: 'right' })}>
                    Right
                  </button>

              )}

                <button
                  onClick={() =>
                    this.setState({
                      slidesToShow: this.state.slidesToShow > 1.0 ? 1.0 : 1.25
                    })
                  }
                >
                  Toggle Partially Visible Slides
                </button>
                <button
                  onClick={() =>
                    this.setState({
                      heightMode:
                        this.state.heightMode === 'current' ? 'max' : 'current'
                    })
                  }
                >
                  Toggle Height Mode Current
                </button>
                <button
                  onClick={() =>
                    this.setState({
                      withoutControls: !this.state.withoutControls
                    })
                  }
                >
                  Toggle Controls
                </button>

              {this.state.animation === 'zoom' && (
                <input
                  type="number"
                  value={this.state.zoomScale}
                  onChange={this.handleZoomScaleChange}
                />
              )}
              <button
                onClick={() =>
                  this.setState({
                    animation:
                      this.state.animation === 'zoom' ? undefined : 'zoom',
                    cellAlign: 'center'
                  })
                }
              >
                Toggle Zoom Animation{' '}
                {this.state.animation === 'zoom' ? 'Off' : 'On'}
              </button>

          </>
        )}

    );
  }
}

ReactDom.render(<App />, document.getElementById('content'));
<|endoftext|>


<commit_before>(function(win, TableFilter){

    var tf = new TableFilter('demo', {
        base_path: '../dist/tablefilter/'
    });
    tf.init();

    module('Sanity checks');
    test('Filters', function() {
        deepEqual(tf instanceof TableFilter, true, 'TableFilter instanciated');
        notEqual(tf.getFilterElement(0), null, 'Filter generated');
    });

    module('Operators');
    test('Regex operator - rgx:', function() {
        tf.setFilterValue(3, 'rgx:[4-5]');
        tf.filter();
        var filteredData = tf.getFilteredData();
        deepEqual(tf.getValidRows().length, 3, 'Expected number of matches');
        deepEqual(
            filteredData[1],
            [3, ['Sydney', 'Brisbane', '982', '1.5', '16']],
            'Expected row data'
        );
    });

    test('Regex operator - rgx:b.*e', function() {
        tf.clearFilters();
        tf.setFilterValue(1, 'rgx:b.*e');
        tf.filter();
        var filteredData = tf.getFilteredData();
        deepEqual(tf.getValidRows().length, 4, 'Expected number of matches');
        deepEqual(
            filteredData[3],
            [8, ['Adelaide', 'Brisbane', '2045', '2.15', '40']],
            'Expected row data'
        );
    });

    test('Regex operator - rgx:b.+e', function() {
        tf.clearFilters();
        tf.setFilterValue(1, 'rgx:b.+e');
        tf.filter();
        var filteredData = tf.getFilteredData();
        deepEqual(tf.getValidRows().length, 3, 'Expected number of matches');
        deepEqual(
            filteredData[2],
            [8, ['Adelaide', 'Brisbane', '2045', '2.15', '40']],
            'Expected row data'
        );
    });

    test('Empty operator - [empty]', function() {
        tf.clearFilters();
        tf.setFilterValue(4, '[empty]');
        tf.filter();
        var filteredData = tf.getFilteredData();
        deepEqual(tf.getValidRows().length, 1, 'Expected number of matches');
        deepEqual(
            filteredData[0],
            [6, ['Adelaide', 'Perth', '2781', '3.1', '']],
            'Expected row data'
        );
    });

    test('Non-empty operator - [nonempty]', function() {
        tf.clearFilters();
        tf.setFilterValue(4, '[nonempty]');
        tf.filter();
        var filteredData = tf.getFilteredData();
        deepEqual(tf.getValidRows().length, 6, 'Expected number of matches');
        deepEqual(
            filteredData[5],
            [8, ['Adelaide', 'Brisbane', '2045', '2.15', '40']],
            'Expected row data');
    });

    test('Or operator - ||', function() {
        tf.clearFilters();
        tf.setFilterValue(1, 'Canberra||Alice Springs');
        tf.filter();
        var filteredData = tf.getFilteredData();
        deepEqual(tf.getValidRows().length, 2, 'Expected number of matches');
        deepEqual(
            filteredData[1],
            [7, ['Adelaide', 'Alice Springs', '1533', '2', '20.25']],
            'Expected row data'
        );
    });

    test('And operator - &&', function() {
        tf.clearFilters();
        tf.setFilterValue(1, 'Al&&Spr');
        tf.filter();
        var filteredData = tf.getFilteredData();
        deepEqual(tf.getValidRows().length, 1, 'Expected number of matches');
        deepEqual(
            filteredData[0],
            [7, ['Adelaide', 'Alice Springs', '1533', '2', '20.25']],
            'Expected row data'
        );
    });

    test('Greater than operator - >', function() {
        tf.clearFilters();
        tf.setFilterValue(2, '>1600');
        tf.filter();
        var filteredData = tf.getFilteredData();
        deepEqual(tf.getValidRows().length, 2, 'Expected number of matches');
        deepEqual(
            filteredData[1],
            [8, ['Adelaide', 'Brisbane', '2045', '2.15', '40']],
            'Expected row data'
        );
    });

    test('Lower than operator - <', function() {
        tf.clearFilters();
        tf.setFilterValue(3, '<1.5');
        tf.filter();
        var filteredData = tf.getFilteredData();
        deepEqual(tf.getValidRows().length, 3, 'Expected number of matches');
        deepEqual(
            filteredData[2],
            [5, ['Sydney', 'Melbourne', '872', '1.1', '10.5']],
            'Expected row data'
        );
    });

    test('Greater than or equal to operator - >=', function() {
        tf.clearFilters();
        tf.setFilterValue(2, '>=2781');
        tf.filter();
        var filteredData = tf.getFilteredData();
        deepEqual(tf.getValidRows().length, 1, 'Expected number of matches');
        deepEqual(
            filteredData[0],
            [6, ['Adelaide', 'Perth', '2781', '3.1', '']],
            'Expected row data'
        );
    });

    test('Lower than or equal to operator - <=', function() {
        tf.clearFilters();
        tf.setFilterValue(3, '<=0.6');
        tf.filter();
        var filteredData = tf.getFilteredData();
        deepEqual(tf.getValidRows().length, 1, 'Expected number of matches');
        deepEqual(
            filteredData[0],
            [4, ['Sydney', 'Canberra', '286', '.6', '4.3']],
            'Expected row data'
        );
    });

    test('Different from operator - !', function() {
        tf.clearFilters();
        tf.setFilterValue(0, '!Sydney');
        tf.filter();
        var filteredData = tf.getFilteredData();
        deepEqual(tf.getValidRows().length, 3, 'Expected number of matches');
        deepEqual(
            filteredData[2],
            [8, ['Adelaide', 'Brisbane', '2045', '2.15', '40']],
            'Expected row data'
        );
    });

    test('Like operator - *', function() {
        tf.clearFilters();
        tf.setFilterValue(1, '*our');
        tf.filter();
        var filteredData = tf.getFilteredData();
        deepEqual(tf.getValidRows().length, 1, 'Expected number of matches');
        deepEqual(
            filteredData[0],
            [5, ['Sydney', 'Melbourne', '872', '1.1', '10.5']],
            'Expected row data'
        );
    });

    test('Equal operator - =', function() {
        tf.clearFilters();
        tf.setFilterValue(1, '=Pe');
        tf.filter();
        deepEqual(tf.getValidRows().length, 0, 'Expected number of matches');

        tf.clearFilters();
        tf.setFilterValue(1, '=Perth');
        tf.filter();
        var filteredData = tf.getFilteredData();
        deepEqual(tf.getValidRows().length, 1, 'Expected number of matches');
        deepEqual(
            filteredData[0],
            [6, ['Adelaide', 'Perth', '2781', '3.1', '']],
            'Expected row data'
        );
    });

    test('Starts with - {', function() {
        tf.clearFilters();
        tf.setFilterValue(1, '{a');
        tf.filter();
        var filteredData = tf.getFilteredData();
        deepEqual(tf.getValidRows().length, 2, 'Expected number of matches');
        deepEqual(
            filteredData[0],
            [2, ['Sydney', 'Adelaide', '1412', '1.4', '25.3']],
            'Expected row data'
        );
    });

    test('Ends with - }', function() {
        tf.clearFilters();
        tf.setFilterValue(1, '}e');
        tf.filter();
        var filteredData = tf.getFilteredData();
        deepEqual(tf.getValidRows().length, 4, 'Expected number of matches');
        deepEqual(
            filteredData[0],
            [2, ['Sydney', 'Adelaide', '1412', '1.4', '25.3']],
            'Expected row data'
        );
    });

    module('Tear-down');
    test('can destroy TableFilter DOM elements', function() {
        tf.destroy();
        deepEqual(tf.isInitialized(), false, 'Filters removed');
    });

})(window, TableFilter);
<commit_msg>Clear filters so the page after tests shows all rows<commit_after>(function(win, TableFilter){

    var tf = new TableFilter('demo', {
        base_path: '../dist/tablefilter/'
    });
    tf.init();

    module('Sanity checks');
    test('Filters', function() {
        deepEqual(tf instanceof TableFilter, true, 'TableFilter instanciated');
        notEqual(tf.getFilterElement(0), null, 'Filter generated');
    });

    module('Operators');
    test('Regex operator - rgx:', function() {
        tf.setFilterValue(3, 'rgx:[4-5]');
        tf.filter();
        var filteredData = tf.getFilteredData();
        deepEqual(tf.getValidRows().length, 3, 'Expected number of matches');
        deepEqual(
            filteredData[1],
            [3, ['Sydney', 'Brisbane', '982', '1.5', '16']],
            'Expected row data'
        );
    });

    test('Regex operator - rgx:b.*e', function() {
        tf.clearFilters();
        tf.setFilterValue(1, 'rgx:b.*e');
        tf.filter();
        var filteredData = tf.getFilteredData();
        deepEqual(tf.getValidRows().length, 4, 'Expected number of matches');
        deepEqual(
            filteredData[3],
            [8, ['Adelaide', 'Brisbane', '2045', '2.15', '40']],
            'Expected row data'
        );
    });

    test('Regex operator - rgx:b.+e', function() {
        tf.clearFilters();
        tf.setFilterValue(1, 'rgx:b.+e');
        tf.filter();
        var filteredData = tf.getFilteredData();
        deepEqual(tf.getValidRows().length, 3, 'Expected number of matches');
        deepEqual(
            filteredData[2],
            [8, ['Adelaide', 'Brisbane', '2045', '2.15', '40']],
            'Expected row data'
        );
    });

    test('Empty operator - [empty]', function() {
        tf.clearFilters();
        tf.setFilterValue(4, '[empty]');
        tf.filter();
        var filteredData = tf.getFilteredData();
        deepEqual(tf.getValidRows().length, 1, 'Expected number of matches');
        deepEqual(
            filteredData[0],
            [6, ['Adelaide', 'Perth', '2781', '3.1', '']],
            'Expected row data'
        );
    });

    test('Non-empty operator - [nonempty]', function() {
        tf.clearFilters();
        tf.setFilterValue(4, '[nonempty]');
        tf.filter();
        var filteredData = tf.getFilteredData();
        deepEqual(tf.getValidRows().length, 6, 'Expected number of matches');
        deepEqual(
            filteredData[5],
            [8, ['Adelaide', 'Brisbane', '2045', '2.15', '40']],
            'Expected row data');
    });

    test('Or operator - ||', function() {
        tf.clearFilters();
        tf.setFilterValue(1, 'Canberra||Alice Springs');
        tf.filter();
        var filteredData = tf.getFilteredData();
        deepEqual(tf.getValidRows().length, 2, 'Expected number of matches');
        deepEqual(
            filteredData[1],
            [7, ['Adelaide', 'Alice Springs', '1533', '2', '20.25']],
            'Expected row data'
        );
    });

    test('And operator - &&', function() {
        tf.clearFilters();
        tf.setFilterValue(1, 'Al&&Spr');
        tf.filter();
        var filteredData = tf.getFilteredData();
        deepEqual(tf.getValidRows().length, 1, 'Expected number of matches');
        deepEqual(
            filteredData[0],
            [7, ['Adelaide', 'Alice Springs', '1533', '2', '20.25']],
            'Expected row data'
        );
    });

    test('Greater than operator - >', function() {
        tf.clearFilters();
        tf.setFilterValue(2, '>1600');
        tf.filter();
        var filteredData = tf.getFilteredData();
        deepEqual(tf.getValidRows().length, 2, 'Expected number of matches');
        deepEqual(
            filteredData[1],
            [8, ['Adelaide', 'Brisbane', '2045', '2.15', '40']],
            'Expected row data'
        );
    });

    test('Lower than operator - <', function() {
        tf.clearFilters();
        tf.setFilterValue(3, '<1.5');
        tf.filter();
        var filteredData = tf.getFilteredData();
        deepEqual(tf.getValidRows().length, 3, 'Expected number of matches');
        deepEqual(
            filteredData[2],
            [5, ['Sydney', 'Melbourne', '872', '1.1', '10.5']],
            'Expected row data'
        );
    });

    test('Greater than or equal to operator - >=', function() {
        tf.clearFilters();
        tf.setFilterValue(2, '>=2781');
        tf.filter();
        var filteredData = tf.getFilteredData();
        deepEqual(tf.getValidRows().length, 1, 'Expected number of matches');
        deepEqual(
            filteredData[0],
            [6, ['Adelaide', 'Perth', '2781', '3.1', '']],
            'Expected row data'
        );
    });

    test('Lower than or equal to operator - <=', function() {
        tf.clearFilters();
        tf.setFilterValue(3, '<=0.6');
        tf.filter();
        var filteredData = tf.getFilteredData();
        deepEqual(tf.getValidRows().length, 1, 'Expected number of matches');
        deepEqual(
            filteredData[0],
            [4, ['Sydney', 'Canberra', '286', '.6', '4.3']],
            'Expected row data'
        );
    });

    test('Different from operator - !', function() {
        tf.clearFilters();
        tf.setFilterValue(0, '!Sydney');
        tf.filter();
        var filteredData = tf.getFilteredData();
        deepEqual(tf.getValidRows().length, 3, 'Expected number of matches');
        deepEqual(
            filteredData[2],
            [8, ['Adelaide', 'Brisbane', '2045', '2.15', '40']],
            'Expected row data'
        );
    });

    test('Like operator - *', function() {
        tf.clearFilters();
        tf.setFilterValue(1, '*our');
        tf.filter();
        var filteredData = tf.getFilteredData();
        deepEqual(tf.getValidRows().length, 1, 'Expected number of matches');
        deepEqual(
            filteredData[0],
            [5, ['Sydney', 'Melbourne', '872', '1.1', '10.5']],
            'Expected row data'
        );
    });

    test('Equal operator - =', function() {
        tf.clearFilters();
        tf.setFilterValue(1, '=Pe');
        tf.filter();
        deepEqual(tf.getValidRows().length, 0, 'Expected number of matches');

        tf.clearFilters();
        tf.setFilterValue(1, '=Perth');
        tf.filter();
        var filteredData = tf.getFilteredData();
        deepEqual(tf.getValidRows().length, 1, 'Expected number of matches');
        deepEqual(
            filteredData[0],
            [6, ['Adelaide', 'Perth', '2781', '3.1', '']],
            'Expected row data'
        );
    });

    test('Starts with - {', function() {
        tf.clearFilters();
        tf.setFilterValue(1, '{a');
        tf.filter();
        var filteredData = tf.getFilteredData();
        deepEqual(tf.getValidRows().length, 2, 'Expected number of matches');
        deepEqual(
            filteredData[0],
            [2, ['Sydney', 'Adelaide', '1412', '1.4', '25.3']],
            'Expected row data'
        );
    });

    test('Ends with - }', function() {
        tf.clearFilters();
        tf.setFilterValue(1, '}e');
        tf.filter();
        var filteredData = tf.getFilteredData();
        deepEqual(tf.getValidRows().length, 4, 'Expected number of matches');
        deepEqual(
            filteredData[0],
            [2, ['Sydney', 'Adelaide', '1412', '1.4', '25.3']],
            'Expected row data'
        );
    });

    module('Tear-down');
    test('can destroy TableFilter DOM elements', function() {
        tf.clearFilters();
        tf.destroy();
        deepEqual(tf.isInitialized(), false, 'Filters removed');
    });

})(window, TableFilter);
<|endoftext|>


<commit_before>const bindAll = require("lodash/fp/bindAll")
const TravisRepo = require("../models/travis-repo")
const fs = require("fs-extra")
const path = require("path")

class TravisService {
  constructor({ githubToken, travisToken, travisEnabled, travisJsonFile, spinner }) {
    bindAll(Object.getOwnPropertyNames(TravisService.prototype), this)
    this.githubToken = githubToken
    this.travisToken = travisToken
    this.travisEnabled = travisEnabled
    this.travisJsonFile = travisJsonFile
    if (this.travisEnabled && !this.githubToken) throw new Error("Missing githubToken in config")
    this.travisEnv = this.parseBeekeeperTravisJson().env
    this.spinner = spinner
  }

  parseBeekeeperTravisJson() {
    const filePath = this.travisJsonFile
    try {
      fs.accessSync(filePath, fs.constants.F_OK | fs.constants.R_OK)
    } catch (e) {
      return {}
    }
    return fs.readJsonSync(filePath)
  }

  async configure({ projectName, projectOwner, isPrivate }) {
    if (!this.travisEnabled) return
    const { githubToken, spinner, travisToken } = this
    const travisRepo = new TravisRepo({ projectName, projectOwner, isPrivate, githubToken, travisToken })
    if (spinner) spinner.start("Travis: Enabling repository")
    await travisRepo.init()
    await travisRepo.enable()
    if (spinner) spinner.log("Travis: Repository enabled")
    if (spinner) spinner.start("Travis: Updating environment variables")
    await travisRepo.updateAllEnv(this.travisEnv)
    if (spinner) spinner.log("Travis: Environment variables updated")
  }
}

module.exports = TravisService
<commit_msg>remove path<commit_after>const bindAll = require("lodash/fp/bindAll")
const TravisRepo = require("../models/travis-repo")
const fs = require("fs-extra")

class TravisService {
  constructor({ githubToken, travisToken, travisEnabled, travisJsonFile, spinner }) {
    bindAll(Object.getOwnPropertyNames(TravisService.prototype), this)
    this.githubToken = githubToken
    this.travisToken = travisToken
    this.travisEnabled = travisEnabled
    this.travisJsonFile = travisJsonFile
    if (this.travisEnabled && !this.githubToken) throw new Error("Missing githubToken in config")
    this.travisEnv = this.parseBeekeeperTravisJson().env
    this.spinner = spinner
  }

  parseBeekeeperTravisJson() {
    const filePath = this.travisJsonFile
    try {
      fs.accessSync(filePath, fs.constants.F_OK | fs.constants.R_OK)
    } catch (e) {
      return {}
    }
    return fs.readJsonSync(filePath)
  }

  async configure({ projectName, projectOwner, isPrivate }) {
    if (!this.travisEnabled) return
    const { githubToken, spinner, travisToken } = this
    const travisRepo = new TravisRepo({ projectName, projectOwner, isPrivate, githubToken, travisToken })
    if (spinner) spinner.start("Travis: Enabling repository")
    await travisRepo.init()
    await travisRepo.enable()
    if (spinner) spinner.log("Travis: Repository enabled")
    if (spinner) spinner.start("Travis: Updating environment variables")
    await travisRepo.updateAllEnv(this.travisEnv)
    if (spinner) spinner.log("Travis: Environment variables updated")
  }
}

module.exports = TravisService
<|endoftext|>


<commit_before>/*
Language: R
Description: R is a free software environment for statistical computing and graphics.
Author: Joe Cheng <joe@rstudio.org>
Contributors: Konrad Rudolph <konrad.rudolph@gmail.com>
Website: https://www.r-project.org
Category: scientific
*/

export default function(hljs) {
  // Identifiers in R cannot start with `_`, but they can start with `.` if it
  // is not immediately followed by a digit.
  // R also supports quoted identifiers, which are near-arbitrary sequences
  // delimited by backticks (`…`), which may contain escape sequences. These are
  // handled in a separate mode. See `test/markup/r/names.txt` for examples.
  // FIXME: Support Unicode identifiers.
  const IDENT_RE = /(?:(?:[a-zA-Z]|\.[._a-zA-Z])[._a-zA-Z0-9]*)|\.(?!\d)/;

  return {
    name: 'R',

    keywords: {
      $pattern: IDENT_RE,
      keyword:
        'function if in break next repeat else for while',
      literal:
        'NULL NA TRUE FALSE Inf NaN NA_integer_|10 NA_real_|10 ' +
        'NA_character_|10 NA_complex_|10',
      built_in:
        // Builtin constants
        'LETTERS letters month.abb month.name pi T F ' +
        // Primitive functions
        // These are all the functions in `base` that are implemented as a
        // `.Primitive`, minus those functions that are also keywords.
        'abs acos acosh all any anyNA Arg as.call as.character ' +
        'as.complex as.double as.environment as.integer as.logical ' +
        'as.null.default as.numeric as.raw asin asinh atan atanh attr ' +
        'attributes baseenv browser c call ceiling class Conj cos cosh ' +
        'cospi cummax cummin cumprod cumsum digamma dim dimnames ' +
        'emptyenv exp expression floor forceAndCall gamma gc.time ' +
        'globalenv Im interactive invisible is.array is.atomic is.call ' +
        'is.character is.complex is.double is.environment is.expression ' +
        'is.finite is.function is.infinite is.integer is.language ' +
        'is.list is.logical is.matrix is.na is.name is.nan is.null ' +
        'is.numeric is.object is.pairlist is.raw is.recursive is.single ' +
        'is.symbol lazyLoadDBfetch length lgamma list log max min ' +
        'missing Mod names nargs nzchar oldClass on.exit pos.to.env ' +
        'proc.time prod quote range Re rep retracemem return round ' +
        'seq_along seq_len seq.int sign signif sin sinh sinpi sqrt ' +
        'standardGeneric substitute sum switch tan tanh tanpi tracemem ' +
        'trigamma trunc unclass untracemem UseMethod xtfrm',
    },

    contains: [
      // Roxygen comments
      hljs.COMMENT(
        /#'/,
        /$/,
        {
          contains: [
            {
              // Handle `@examples` separately to cause all subsequent code
              // until the next `@`-tag on its own line to be kept as-is,
              // preventing highlighting. This code is example R code, so nested
              // doctags shouldn’t be treated as such. See
              // `test/markup/r/roxygen.txt` for an example.
              className: 'doctag',
              begin: '@examples',
              starts: {
                contains: [
                  { begin: /\n/ },
                  {
                    begin: /#'\s*(?=@[a-zA-Z]+)/,
                    endsParent: true,
                  },
                  {
                    begin: /#'/,
                    end: /$/,
                    excludeBegin: true,
                  }
                ]
              }
            },
            {
              // Handle `@param` to highlight the parameter name following
              // after.
              className: 'doctag',
              begin: '@param',
              end: /$/,
              contains: [
                {
                  className: 'variable',
                  variants: [
                    { begin: IDENT_RE },
                    { begin: /`(?:\\.|[^`])+`/ }
                  ],
                  endsParent: true
                }
              ]
            },
            {
              className: 'doctag',
              begin: /@[a-zA-Z]+/
            },
            {
              className: 'meta-keyword',
              begin: /\\[a-zA-Z]+/,
            }
          ]
        }
      ),

      hljs.HASH_COMMENT_MODE,

      {
        className: 'string',
        contains: [hljs.BACKSLASH_ESCAPE],
        variants: [
          hljs.END_SAME_AS_BEGIN({ begin: /[rR]"(-*)\(/, end: /\)(-*)"/ }),
          hljs.END_SAME_AS_BEGIN({ begin: /[rR]"(-*)\{/, end: /\}(-*)"/ }),
          hljs.END_SAME_AS_BEGIN({ begin: /[rR]"(-*)\[/, end: /\](-*)"/ }),
          hljs.END_SAME_AS_BEGIN({ begin: /[rR]'(-*)\(/, end: /\)(-*)'/ }),
          hljs.END_SAME_AS_BEGIN({ begin: /[rR]'(-*)\{/, end: /\}(-*)'/ }),
          hljs.END_SAME_AS_BEGIN({ begin: /[rR]'(-*)\[/, end: /\](-*)'/ }),
          {begin: '"', end: '"', relevance: 0},
          {begin: "'", end: "'", relevance: 0}
        ],
      },
      {
        className: 'number',
        variants: [
          // TODO: replace with negative look-behind when available
          // { begin: /(?<![a-zA-Z0-9._])0[xX][0-9a-fA-F]+\.[0-9a-fA-F]*[pP][+-]?\d+i?/ },
          // { begin: /(?<![a-zA-Z0-9._])0[xX][0-9a-fA-F]+([pP][+-]?\d+)?[Li]?/ },
          // { begin: /(?<![a-zA-Z0-9._])(\d+(\.\d*)?|\.\d+)([eE][+-]?\d+)?[Li]?/ }

          // The below rules all eat an extra character in front (for the
          // look-behind check) and then exclude it from the match, but I think
          // in many cases this will work out just fine.
          {
            // Special case: only hexadecimal binary powers can contain fractions.
            begin: /([^a-zA-Z0-9._])(?=0[xX][0-9a-fA-F]+\.[0-9a-fA-F]*[pP][+-]?\d+i?)/,
            end: /0[xX][0-9a-fA-F]+\.[0-9a-fA-F]*[pP][+-]?\d+i?/,
            excludeBegin: true
          },
          {
            begin: /([^a-zA-Z0-9._])(?=0[xX][0-9a-fA-F]+([pP][+-]?\d+)?[Li]?)/,
            end: /0[xX][0-9a-fA-F]+([pP][+-]?\d+)?[Li]?/ ,
            excludeBegin: true
          },
          {
            begin: /([^a-zA-Z0-9._])(?=(\d+(\.\d*)?|\.\d+)([eE][+-]?\d+)?[Li]?)/,
            end: /(\d+(\.\d*)?|\.\d+)([eE][+-]?\d+)?[Li]?/,
            excludeBegin: true
          }
        ],
        // "on:begin": (match, response) => {
        //   if (match.index > 0) {
        //     let priorChar = match.input[match.index - 1];
        //     if (priorChar.match(/[a-zA-Z0-9._]/)) response.ignoreMatch();
        //   }
        // },
        relevance: 0
      },

      {
        // infix operator
        begin: '%',
        end: '%'
      },

      {
        // escaped identifier
        begin: '`',
        end: '`',
        contains: [
          { begin: /\\./ }
        ]
      }
    ]
  };
}
<commit_msg>enh(autodetect) r: detect `<-`, illegal: `->`<commit_after>/*
Language: R
Description: R is a free software environment for statistical computing and graphics.
Author: Joe Cheng <joe@rstudio.org>
Contributors: Konrad Rudolph <konrad.rudolph@gmail.com>
Website: https://www.r-project.org
Category: scientific
*/

import * as regex from '../lib/regex.js';

export default function(hljs) {
  // Identifiers in R cannot start with `_`, but they can start with `.` if it
  // is not immediately followed by a digit.
  // R also supports quoted identifiers, which are near-arbitrary sequences
  // delimited by backticks (`…`), which may contain escape sequences. These are
  // handled in a separate mode. See `test/markup/r/names.txt` for examples.
  // FIXME: Support Unicode identifiers.
  const IDENT_RE = /(?:(?:[a-zA-Z]|\.[._a-zA-Z])[._a-zA-Z0-9]*)|\.(?!\d)/;
  const SIMPLE_IDENT = /[a-zA-Z][a-zA-Z_0-9]*/;

  return {
    name: 'R',

    // only in Haskell, not R
    illegal: /->/,
    keywords: {
      $pattern: IDENT_RE,
      keyword:
        'function if in break next repeat else for while',
      literal:
        'NULL NA TRUE FALSE Inf NaN NA_integer_|10 NA_real_|10 ' +
        'NA_character_|10 NA_complex_|10',
      built_in:
        // Builtin constants
        'LETTERS letters month.abb month.name pi T F ' +
        // Primitive functions
        // These are all the functions in `base` that are implemented as a
        // `.Primitive`, minus those functions that are also keywords.
        'abs acos acosh all any anyNA Arg as.call as.character ' +
        'as.complex as.double as.environment as.integer as.logical ' +
        'as.null.default as.numeric as.raw asin asinh atan atanh attr ' +
        'attributes baseenv browser c call ceiling class Conj cos cosh ' +
        'cospi cummax cummin cumprod cumsum digamma dim dimnames ' +
        'emptyenv exp expression floor forceAndCall gamma gc.time ' +
        'globalenv Im interactive invisible is.array is.atomic is.call ' +
        'is.character is.complex is.double is.environment is.expression ' +
        'is.finite is.function is.infinite is.integer is.language ' +
        'is.list is.logical is.matrix is.na is.name is.nan is.null ' +
        'is.numeric is.object is.pairlist is.raw is.recursive is.single ' +
        'is.symbol lazyLoadDBfetch length lgamma list log max min ' +
        'missing Mod names nargs nzchar oldClass on.exit pos.to.env ' +
        'proc.time prod quote range Re rep retracemem return round ' +
        'seq_along seq_len seq.int sign signif sin sinh sinpi sqrt ' +
        'standardGeneric substitute sum switch tan tanh tanpi tracemem ' +
        'trigamma trunc unclass untracemem UseMethod xtfrm',
    },

    contains: [
      // Roxygen comments
      hljs.COMMENT(
        /#'/,
        /$/,
        {
          contains: [
            {
              // Handle `@examples` separately to cause all subsequent code
              // until the next `@`-tag on its own line to be kept as-is,
              // preventing highlighting. This code is example R code, so nested
              // doctags shouldn’t be treated as such. See
              // `test/markup/r/roxygen.txt` for an example.
              className: 'doctag',
              begin: '@examples',
              starts: {
                contains: [
                  { begin: /\n/ },
                  {
                    begin: /#'\s*(?=@[a-zA-Z]+)/,
                    endsParent: true,
                  },
                  {
                    begin: /#'/,
                    end: /$/,
                    excludeBegin: true,
                  }
                ]
              }
            },
            {
              // Handle `@param` to highlight the parameter name following
              // after.
              className: 'doctag',
              begin: '@param',
              end: /$/,
              contains: [
                {
                  className: 'variable',
                  variants: [
                    { begin: IDENT_RE },
                    { begin: /`(?:\\.|[^`])+`/ }
                  ],
                  endsParent: true
                }
              ]
            },
            {
              className: 'doctag',
              begin: /@[a-zA-Z]+/
            },
            {
              className: 'meta-keyword',
              begin: /\\[a-zA-Z]+/,
            }
          ]
        }
      ),

      hljs.HASH_COMMENT_MODE,

      {
        className: 'string',
        contains: [hljs.BACKSLASH_ESCAPE],
        variants: [
          hljs.END_SAME_AS_BEGIN({ begin: /[rR]"(-*)\(/, end: /\)(-*)"/ }),
          hljs.END_SAME_AS_BEGIN({ begin: /[rR]"(-*)\{/, end: /\}(-*)"/ }),
          hljs.END_SAME_AS_BEGIN({ begin: /[rR]"(-*)\[/, end: /\](-*)"/ }),
          hljs.END_SAME_AS_BEGIN({ begin: /[rR]'(-*)\(/, end: /\)(-*)'/ }),
          hljs.END_SAME_AS_BEGIN({ begin: /[rR]'(-*)\{/, end: /\}(-*)'/ }),
          hljs.END_SAME_AS_BEGIN({ begin: /[rR]'(-*)\[/, end: /\](-*)'/ }),
          {begin: '"', end: '"', relevance: 0},
          {begin: "'", end: "'", relevance: 0}
        ],
      },
      {
        className: 'number',
        variants: [
          // TODO: replace with negative look-behind when available
          // { begin: /(?<![a-zA-Z0-9._])0[xX][0-9a-fA-F]+\.[0-9a-fA-F]*[pP][+-]?\d+i?/ },
          // { begin: /(?<![a-zA-Z0-9._])0[xX][0-9a-fA-F]+([pP][+-]?\d+)?[Li]?/ },
          // { begin: /(?<![a-zA-Z0-9._])(\d+(\.\d*)?|\.\d+)([eE][+-]?\d+)?[Li]?/ }

          // The below rules all eat an extra character in front (for the
          // look-behind check) and then exclude it from the match, but I think
          // in many cases this will work out just fine.
          {
            // Special case: only hexadecimal binary powers can contain fractions.
            begin: /([^a-zA-Z0-9._])(?=0[xX][0-9a-fA-F]+\.[0-9a-fA-F]*[pP][+-]?\d+i?)/,
            end: /0[xX][0-9a-fA-F]+\.[0-9a-fA-F]*[pP][+-]?\d+i?/,
            excludeBegin: true
          },
          {
            begin: /([^a-zA-Z0-9._])(?=0[xX][0-9a-fA-F]+([pP][+-]?\d+)?[Li]?)/,
            end: /0[xX][0-9a-fA-F]+([pP][+-]?\d+)?[Li]?/ ,
            excludeBegin: true
          },
          {
            begin: /([^a-zA-Z0-9._])(?=(\d+(\.\d*)?|\.\d+)([eE][+-]?\d+)?[Li]?)/,
            end: /(\d+(\.\d*)?|\.\d+)([eE][+-]?\d+)?[Li]?/,
            excludeBegin: true
          }
        ],
        // "on:begin": (match, response) => {
        //   if (match.index > 0) {
        //     let priorChar = match.input[match.index - 1];
        //     if (priorChar.match(/[a-zA-Z0-9._]/)) response.ignoreMatch();
        //   }
        // },
        relevance: 0
      },

      {
        // infix operator
        begin: '%',
        end: '%'
      },
      // relevance boost for assignment
      {
        begin: regex.concat(SIMPLE_IDENT, "\\s+<-\\s+")
      },
      {
        // escaped identifier
        begin: '`',
        end: '`',
        contains: [
          { begin: /\\./ }
        ]
      }
    ]
  };
}
<|endoftext|>


<commit_before>const chai = require('chai')
const expect = chai.expect
const sinon = require('sinon')
chai.use(require("sinon-chai"))
chai.use(require('chai-as-promised'))
const Promise = require('bluebird')
const FirePush = require('..').default
const RequestHelper = require('../dist/src/request-helper').default;

describe('#Fire-Push Test', function () {
	it('Api key equal when is setted in constructor and when is getted', function () {
		let apiKey = '17272829823'
		let firePush = new FirePush({apiKey})
		expect(apiKey).to.be.equal(firePush.configRequest.apiKey)
	})

	describe('#sendMessage', function() {
		let apiKey
		let firePush
		let sandbox
		let message

		beforeEach(() => {
			sandbox = sinon.sandbox.create();
			firePush = new FirePush({apiKey});
			apiKey = '17272829823';
			message = {
				notification : {
					title : 'Ey',
					body : 'Make push notifications great again',
					sound : 'cielitolindo'
				},
				to : 'topics/user_1'
			};
		});

		afterEach(()=>{
			sandbox.restore();
		});

		it('message method must be fullfilled', function () {
			let requestSenderStub = sandbox.stub(RequestHelper.prototype, 'sendMessage')
			requestSenderStub.returns(Promise.resolve({ message_id : "1929292"}))
			let promiseSended = firePush.sendMessage(message)
			expect(requestSenderStub).to.have.been.called
			expect(promiseSended).to.be.fulfilled
			expect(promiseSended).to.eventually.have.property("message_id")
		});
	});
});<commit_msg>#Fire-Push Test #sendMessage message method must be reject when notification property is null<commit_after>const chai = require('chai')
const expect = chai.expect
const sinon = require('sinon')
chai.use(require("sinon-chai"))
chai.use(require('chai-as-promised'))
const Promise = require('bluebird')
const FirePush = require('..').default
const RequestHelper = require('../dist/src/request-helper').default;

describe('#Fire-Push Test', function () {
	it('Api key equal when is setted in constructor and when is getted', function () {
		let apiKey = '17272829823'
		let firePush = new FirePush({apiKey})
		expect(apiKey).to.be.equal(firePush.configRequest.apiKey)
	})

	describe('#sendMessage', function() {
		let apiKey
		let firePush
		let sandbox
		let message

		beforeEach(() => {
			sandbox = sinon.sandbox.create();
			firePush = new FirePush({apiKey});
			apiKey = '17272829823';
			message = {
				notification : {
					title : 'Ey',
					body : 'Make push notifications great again',
					sound : 'cielitolindo'
				},
				to : 'topics/user_1'
			};
		});

		afterEach(()=>{
			sandbox.restore();
		});

		it('message method must be fullfilled', function () {
			let requestSenderStub = sandbox.stub(RequestHelper.prototype, 'sendMessage')
			requestSenderStub.returns(Promise.resolve({ message_id : "1929292"}))
			let promiseSended = firePush.sendMessage(message)
			expect(requestSenderStub).to.have.been.called
			expect(promiseSended).to.be.fulfilled
			expect(promiseSended).to.eventually.have.property("message_id")
		});

		it('message method must be reject when "to" property is number', function () {
			message.to = 28382292
			let promiseSended = firePush.sendMessage(message)
			expect(promiseSended).be.rejected
		});

		it('message method must be reject when notification property is null', function () {
			message.notification = null
			let promiseSended = firePush.sendMessage(message)
			expect(promiseSended).be.rejected
		});
	});
});<|endoftext|>