<commit_before>/**
 * @jest-environment node
 */
import fs from 'fs'
import path from 'path'
import env from 'node-env-file'
import { createBrowser } from 'tib'
import { getPort } from '../utils/build'

const browserString = process.env.BROWSER_STRING || 'puppeteer/core'

describe(browserString, () => {
  let browser
  let page
  const folder = path.resolve(__dirname, '..', 'fixtures/basic/.vue-meta/')

  beforeAll(async () => {
    if (browserString.includes('browserstack') && browserString.includes('local')) {
      const envFile = path.resolve(__dirname, '..', '..', '.env-browserstack')
      if (fs.existsSync(envFile)) {
        env(envFile)
      }
    }

    const port = await getPort()

    browser = await createBrowser(browserString, {
      folder,
      staticServer: {
        folder,
        port
      },
      extendPage (page) {
        return {
          async navigate (path) {
            await page.runAsyncScript((path) => {
              return new Promise((resolve) => {
                const oldTitle = document.title

                // local firefox has sometimes not updated the title
                // even when the DOM is supposed to be fully updated
                const waitTitleChanged = function () {
                  setTimeout(function () {
                    if (oldTitle !== document.title) {
                      resolve()
                    } else {
                      waitTitleChanged()
                    }
                  }, 50)
                }

                window.$vueMeta.$once('routeChanged', waitTitleChanged)
                window.$vueMeta.$router.push(path)
              })
            }, path)
          },
          routeData () {
            return page.runScript(() => ({
              path: window.$vueMeta.$route.path,
              query: window.$vueMeta.$route.query
            }))
          }
        }
      }
    })

    // browser.setLogLevel(['warn', 'error', 'log', 'info'])
  })

  afterAll(async () => {
    if (browser) {
      await browser.close()
    }
  })

  test('open page', async () => {
    const url = browser.getUrl('/index.html')

    page = await browser.page(url)

    expect(await page.getAttribute('html', 'data-vue-meta-server-rendered')).toBe(null)
    expect(await page.getAttribute('html', 'lang')).toBe('en')
    expect(await page.getAttribute('html', 'amp')).toBe('')
    expect(await page.getAttribute('html', 'allowfullscreen')).toBe(null)
    expect(await page.getAttribute('head', 'test')).toBe('true')
    expect(await page.getText('h1')).toBe('Basic')
    expect(await page.getText('title')).toBe('Home | Vue Meta Test')
    expect(await page.getElementCount('meta')).toBe(2)

    let sanitizeCheck = await page.getTexts('script')
    sanitizeCheck.push(...(await page.getTexts('noscript')))
    sanitizeCheck = sanitizeCheck.filter(v => !!v)

    expect(sanitizeCheck.length).toBe(4)
    expect(() => JSON.parse(sanitizeCheck[0])).not.toThrow()
    // TODO: check why this doesnt Throw when Home is dynamic loaded
    // (but that causes hydration error)
    expect(() => JSON.parse(sanitizeCheck[1])).toThrow()
    expect(() => JSON.parse(sanitizeCheck[2])).not.toThrow()
    expect(() => JSON.parse(sanitizeCheck[3])).not.toThrow()

    expect(await page.getElementCount('body noscript:first-child')).toBe(1)
    expect(await page.getElementCount('body noscript:last-child')).toBe(1)

    expect(await page.runScript(() => {
      return window.loadTest
    })).toBe('loaded')

    expect(await page.runScript(() => {
      return window.loadCallback
    })).toBe('yes')
  })

  test('/about', async () => {
    try {
      await page.navigate('/about', false)
    } catch (e) {
      if (e.constructor.name !== 'ScriptTimeoutError') {
        throw e
      } else {
        console.warn(e) // eslint-disable-line no-console
      }
    }

    expect(await page.getText('title')).toBe('About')
    expect(await page.getElementCount('meta')).toBe(1)
  })
})
<commit_msg>chore: add browserstack log capabilities<commit_after>/**
 * @jest-environment node
 */
import fs from 'fs'
import path from 'path'
import env from 'node-env-file'
import { createBrowser } from 'tib'
import { getPort } from '../utils/build'

const browserString = process.env.BROWSER_STRING || 'puppeteer/core'

describe(browserString, () => {
  let browser
  let page
  const folder = path.resolve(__dirname, '..', 'fixtures/basic/.vue-meta/')

  beforeAll(async () => {
    if (browserString.includes('browserstack') && browserString.includes('local')) {
      const envFile = path.resolve(__dirname, '..', '..', '.env-browserstack')
      if (fs.existsSync(envFile)) {
        env(envFile)
      }
    }

    const port = await getPort()

    browser = await createBrowser(browserString, {
      folder,
      staticServer: {
        folder,
        port
      },
      extendPage (page) {
        return {
          async navigate (path) {
            await page.runAsyncScript((path) => {
              return new Promise((resolve) => {
                const oldTitle = document.title

                // local firefox has sometimes not updated the title
                // even when the DOM is supposed to be fully updated
                const waitTitleChanged = function () {
                  setTimeout(function () {
                    if (oldTitle !== document.title) {
                      resolve()
                    } else {
                      waitTitleChanged()
                    }
                  }, 50)
                }

                window.$vueMeta.$once('routeChanged', waitTitleChanged)
                window.$vueMeta.$router.push(path)
              })
            }, path)
          },
          routeData () {
            return page.runScript(() => ({
              path: window.$vueMeta.$route.path,
              query: window.$vueMeta.$route.query
            }))
          }
        }
      }
    }, false)

    browser.addCapability('browserstack.console', 'info')
    browser.addCapability('browserstack.networkLogs', 'true')

    await browser.start()

    // browser.setLogLevel(['warn', 'error', 'log', 'info'])
  })

  afterAll(async () => {
    if (browser) {
      await browser.close()
    }
  })

  test('open page', async () => {
    const url = browser.getUrl('/index.html')

    page = await browser.page(url)

    expect(await page.getAttribute('html', 'data-vue-meta-server-rendered')).toBe(null)
    expect(await page.getAttribute('html', 'lang')).toBe('en')
    expect(await page.getAttribute('html', 'amp')).toBe('')
    expect(await page.getAttribute('html', 'allowfullscreen')).toBe(null)
    expect(await page.getAttribute('head', 'test')).toBe('true')
    expect(await page.getText('h1')).toBe('Basic')
    expect(await page.getText('title')).toBe('Home | Vue Meta Test')
    expect(await page.getElementCount('meta')).toBe(2)

    let sanitizeCheck = await page.getTexts('script')
    sanitizeCheck.push(...(await page.getTexts('noscript')))
    sanitizeCheck = sanitizeCheck.filter(v => !!v)

    expect(sanitizeCheck.length).toBe(4)
    expect(() => JSON.parse(sanitizeCheck[0])).not.toThrow()
    // TODO: check why this doesnt Throw when Home is dynamic loaded
    // (but that causes hydration error)
    expect(() => JSON.parse(sanitizeCheck[1])).toThrow()
    expect(() => JSON.parse(sanitizeCheck[2])).not.toThrow()
    expect(() => JSON.parse(sanitizeCheck[3])).not.toThrow()

    expect(await page.getElementCount('body noscript:first-child')).toBe(1)
    expect(await page.getElementCount('body noscript:last-child')).toBe(1)

    expect(await page.runScript(() => {
      return window.loadTest
    })).toBe('loaded')

    expect(await page.runScript(() => {
      return window.loadCallback
    })).toBe('yes')
  })

  test('/about', async () => {
    try {
      await page.navigate('/about', false)
    } catch (e) {
      if (e.constructor.name !== 'ScriptTimeoutError') {
        throw e
      } else {
        console.warn(e) // eslint-disable-line no-console
      }
    }

    expect(await page.getText('title')).toBe('About')
    expect(await page.getElementCount('meta')).toBe(1)
  })
})
<|endoftext|>


<commit_before>import { Buffer } from 'safe-buffer'
import StructType from './StructType'
import { types, getType } from './types'
import getValue from './getValue'

/**
 * @param {Object} descriptor Object describing this Struct, like `{ key: type, key2: type2 }`
 * @return {function()} Buffer decoding function, with StructType properties and an `.encode` method to encode Buffers.
 */
function Struct (descriptor) {
  let fields
  if (descriptor) {
    fields = Object.keys(descriptor).map((key) => ({
      name: key,
      type: getType(descriptor[key])
    }))
  } else {
    fields = []
  }

  /**
   * Decodes a buffer into the object structure as described by this Struct.
   * @param {Object|Buffer} opts A Buffer to decode.
   */
  const decode = (opts, parent) => {
    const struct = {}
    // if there is a parent struct, then we need to start at some offset (namely where this struct starts)
    const subOpts = {
      struct,
      buf: opts.buf,
      offset: opts.offset || 0,
      parent: parent || null
    }

    // `struct` gets a temporary `.$parent` property so dependencies can travel up the chain, like in:
    // ```
    // Struct({
    //   size: int8,
    //   b: Struct({
    //     text1: string('../size'),
    //     text2: string('../size')
    //   })
    // })
    // ```
    // Where ../size needs to access parent structs.
    struct.$parent = parent || null

    fields.forEach((field) => {
      struct[field.name] = field.type.read(subOpts, struct)
    })

    // ensure that the parent continues reading in the right spot
    opts.offset = subOpts.offset

    delete struct.$parent

    return struct
  }

  /**
   * Encodes an object into a Buffer as described by this Struct.
   * @param {Object} struct The Object to encode.
   */
  const encode = (struct) => {
    const size = type.size(struct)
    const buf = Buffer.alloc(size)
    const opts = {
      buf,
      offset: 0
    }

    type.write(opts, struct)

    return buf
  }

  const type = StructType({
    read: decode,
    write (opts, struct) {
      fields.forEach((field) => {
        field.type.write(opts, struct[field.name])
      })
    },
    size (struct) {
      return fields.reduce(
        (size, field) => size + field.type.size(struct[field.name], struct),
        0
      )
    }
  })
  type.encode = encode
  type.field = (name, fieldType) => {
    fields.push({ name, type: getType(fieldType) })
    return type
  }

  return type
}

// import Struct from 'awestruct'
Struct.default = Struct
// import { Type } from 'awestruct', etc
// require('awestruct').Type, etc
Struct.Struct = Struct
Struct.Type = StructType
Struct.types = types
Struct.getValue = getValue
Struct.getType = getType

// require('awestruct') === Struct
module.exports = Struct
<commit_msg>check input type in `.encode()`.<commit_after>import { Buffer } from 'safe-buffer'
import StructType from './StructType'
import { types, getType } from './types'
import getValue from './getValue'

/**
 * @param {Object} descriptor Object describing this Struct, like `{ key: type, key2: type2 }`
 * @return {function()} Buffer decoding function, with StructType properties and an `.encode` method to encode Buffers.
 */
function Struct (descriptor) {
  let fields
  if (descriptor) {
    fields = Object.keys(descriptor).map((key) => ({
      name: key,
      type: getType(descriptor[key])
    }))
  } else {
    fields = []
  }

  /**
   * Decodes a buffer into the object structure as described by this Struct.
   * @param {Object|Buffer} opts A Buffer to decode.
   */
  const decode = (opts, parent) => {
    const struct = {}
    // if there is a parent struct, then we need to start at some offset (namely where this struct starts)
    const subOpts = {
      struct,
      buf: opts.buf,
      offset: opts.offset || 0,
      parent: parent || null
    }

    // `struct` gets a temporary `.$parent` property so dependencies can travel up the chain, like in:
    // ```
    // Struct({
    //   size: int8,
    //   b: Struct({
    //     text1: string('../size'),
    //     text2: string('../size')
    //   })
    // })
    // ```
    // Where ../size needs to access parent structs.
    struct.$parent = parent || null

    fields.forEach((field) => {
      struct[field.name] = field.type.read(subOpts, struct)
    })

    // ensure that the parent continues reading in the right spot
    opts.offset = subOpts.offset

    delete struct.$parent

    return struct
  }

  /**
   * Encodes an object into a Buffer as described by this Struct.
   * @param {Object} struct The Object to encode.
   */
  const encode = (struct) => {
    if (typeof struct !== 'object') {
      throw new TypeError('Expected an object')
    }
    const size = type.size(struct)
    const buf = Buffer.alloc(size)
    const opts = {
      buf,
      offset: 0
    }

    type.write(opts, struct)

    return buf
  }

  const type = StructType({
    read: decode,
    write (opts, struct) {
      fields.forEach((field) => {
        field.type.write(opts, struct[field.name])
      })
    },
    size (struct) {
      return fields.reduce(
        (size, field) => size + field.type.size(struct[field.name], struct),
        0
      )
    }
  })
  type.encode = encode
  type.field = (name, fieldType) => {
    fields.push({ name, type: getType(fieldType) })
    return type
  }

  return type
}

// import Struct from 'awestruct'
Struct.default = Struct
// import { Type } from 'awestruct', etc
// require('awestruct').Type, etc
Struct.Struct = Struct
Struct.Type = StructType
Struct.types = types
Struct.getValue = getValue
Struct.getType = getType

// require('awestruct') === Struct
module.exports = Struct
<|endoftext|>


<commit_before>// Copyright (c) 2011-2012 UX Productivity Pty Ltd. All rights reserved.

var Styles_getCSSText;
var Styles_setCSSText;
var Styles_getBuiltinCSSURL;
var Styles_init;

(function() {

    Styles_getCSSText = trace(function getCSSText()
    {
        var head = DOM_documentHead(document);
        var cssText = "";
        for (var child = head.firstChild; child != null; child = child.nextSibling) {
            if (child._type == HTML_STYLE)
                cssText += getNodeText(child)+"\n";
        }
        return cssText;
    });

    Styles_setCSSText = trace(function setCSSText(cssText)
    {
        UndoManager_newGroup("Update styles");
        var head = DOM_documentHead(document);
        var next;
        for (var child = head.firstChild; child != null; child = next) {
            next = child.nextSibling;
            if (child._type == HTML_STYLE)
                DOM_deleteNode(child);
        }
        var style = DOM_createElement(document,"STYLE");
        DOM_appendChild(style,DOM_createTextNode(document,cssText));
        DOM_appendChild(head,style);
    });

    var addBuiltinStylesheet = trace(function addBuiltinStylesheet(cssURL)
    {
        var head = DOM_documentHead(document);
        for (var child = head.firstChild; child != null; child = child.nextSibling) {
            if ((child._type == HTML_LINK) &&
                (child.getAttribute("rel") == "stylesheet") &&
                (child.getAttribute("href") == cssURL)) {
                // Link element was already added by HTMLInjectionProtocol
                return;
            }
        }

        // HTMLInjectionProtocol was unable to find <head> element and insert the stylesheet link,
        // so add it ourselves
        var link = DOM_createElement(document,"LINK");
        DOM_setAttribute(link,"rel","stylesheet");
        DOM_setAttribute(link,"href",cssURL);
        DOM_insertBefore(head,link,head.firstChild);
    });

    var builtinCSSURL = null;

    Styles_getBuiltinCSSURL = trace(function getBuiltinCSSURL()
    {
        return builtinCSSURL;
    });

    // public
    Styles_init = trace(function init(cssURL)
    {
        if (cssURL != null)
            builtinCSSURL = cssURL;

        if (builtinCSSURL != null)
            addBuiltinStylesheet(builtinCSSURL);
    });

})();
<commit_msg>When the stylesheet has changed, send both the serialised CSS text and also a JSON representation of the stylesheet to the javascript Styles module. This way, we have an easy-to-access representation of the entire stylesheet in javascript code, and don't have to rely on the DOM CSS API or use getComputedStyle() (which is buggy, at least in the case of counters).<commit_after>// Copyright (c) 2011-2012 UX Productivity Pty Ltd. All rights reserved.

var Styles_getCSSText;
var Styles_setCSSText;
var Styles_getBuiltinCSSURL;
var Styles_init;

(function() {

    var rules = new Object();

    Styles_getCSSText = trace(function getCSSText()
    {
        var head = DOM_documentHead(document);
        var cssText = "";
        for (var child = head.firstChild; child != null; child = child.nextSibling) {
            if (child._type == HTML_STYLE)
                cssText += getNodeText(child)+"\n";
        }
        return cssText;
    });

    Styles_setCSSText = trace(function setCSSText(cssText,cssRules)
    {
        UndoManager_newGroup("Update styles");
        var head = DOM_documentHead(document);
        var next;
        for (var child = head.firstChild; child != null; child = next) {
            next = child.nextSibling;
            if (child._type == HTML_STYLE)
                DOM_deleteNode(child);
        }
        var style = DOM_createElement(document,"STYLE");
        DOM_appendChild(style,DOM_createTextNode(document,cssText));
        DOM_appendChild(head,style);
        rules = cssRules; // FIXME: undo support? (must coordinate with ObjC code)
        return {}; // Objective C caller expects JSON result
    });

    var addBuiltinStylesheet = trace(function addBuiltinStylesheet(cssURL)
    {
        var head = DOM_documentHead(document);
        for (var child = head.firstChild; child != null; child = child.nextSibling) {
            if ((child._type == HTML_LINK) &&
                (child.getAttribute("rel") == "stylesheet") &&
                (child.getAttribute("href") == cssURL)) {
                // Link element was already added by HTMLInjectionProtocol
                return;
            }
        }

        // HTMLInjectionProtocol was unable to find <head> element and insert the stylesheet link,
        // so add it ourselves
        var link = DOM_createElement(document,"LINK");
        DOM_setAttribute(link,"rel","stylesheet");
        DOM_setAttribute(link,"href",cssURL);
        DOM_insertBefore(head,link,head.firstChild);
    });

    var builtinCSSURL = null;

    Styles_getBuiltinCSSURL = trace(function getBuiltinCSSURL()
    {
        return builtinCSSURL;
    });

    // public
    Styles_init = trace(function init(cssURL)
    {
        if (cssURL != null)
            builtinCSSURL = cssURL;

        if (builtinCSSURL != null)
            addBuiltinStylesheet(builtinCSSURL);
    });

})();
<|endoftext|>


<commit_before>module.exports = {
  'initial state': (browser) => {
    browser.url('http://localhost:9090')
      .waitForRiot()
      .expect.element('.gb-pager__link.prev.disabled').to.be.present;
    browser.expect.element('.gb-terminal__link.first.disabled').to.be.present;
    browser.expect.element('.gb-terminal__link.last:not(.disabled)').to.be.present;
    browser.expect.element('.gb-pager__link.next:not(.disabled)').to.be.present;
    browser.expect.element('.gb-pages__page.selected').text.to.eq('1');
    browser.expect.element('.gb-pages li:nth-of-type(1) .gb-pages__page').text.to.eq('1');
    browser.expect.element('.gb-pages li:nth-of-type(5) .gb-pages__page').text.to.eq('5');
    browser.elements('css selector', '.gb-pages__page', (result) => {
      browser.assert.equal(result.value.length, 5);
    });
    browser.expect.element('.gb-pages__ellipsis:nth-child(1)').to.not.be.present;
    browser.expect.element('.gb-pages__ellipsis:nth-child(6)').to.be.visible;
    browser.end();
  },

  'go to next page': (browser) => {
    browser.url('http://localhost:9090')
      .waitForRiot()
      .click('.gb-pager__link.next')
      .expect.element('.gb-pager__link.prev:not(.disabled)').to.be.present.before(1000);
    browser.expect.element('.gb-terminal__link.first:not(.disabled)').to.be.present;
    browser.expect.element('.gb-pages__page.selected').text.to.eq('2');
    browser.end();
  },

  'go to previous page': '' + function(browser) {
    browser.url('http://localhost:9090')
      .waitForRiot()
      .click('.gb-pager__link.prev')
      .expect.element('.gb-pager__link.prev.disabled').to.be.present.before(1000);
    browser.expect.element('.gb-terminal__link.first.disabled').to.be.present;
    browser.end();
  },

  'go to first page': '' + function(browser) {
    browser.url('http://localhost:9090')
      .waitForRiot()
      .click('.gb-terminal__link.first')
      .expect.element('.gb-pager__link.prev.disabled').to.be.present.before(1000);
    browser.expect.element('.gb-terminal__link.first.disabled').to.be.present;
    browser.end();
  },

  'go to last page': (browser) => {
    browser.url('http://localhost:9090')
      .waitForRiot()
      .click('.gb-terminal__link.last')
      .expect.element('.gb-pager__link.next.disabled').to.be.present.before(1000);
    browser.expect.element('.gb-terminal__link.last.disabled').to.be.present;
    browser.expect.element('.gb-pages__page.selected').text.to.eq('833');
    browser.expect.element('.gb-pages__ellipsis:nth-child(1)').to.be.visible;
    browser.expect.element('.gb-pages__ellipsis:nth-child(6)').to.not.be.present;
    browser.end();
  },

  'jump to page': (browser) => {
    browser.url('http://localhost:9090')
      .waitForRiot()
      .click('.gb-pages li:nth-of-type(4) .gb-pages__page')
      .expect.element('.gb-pages__page.selected').text.to.eq('4');
    browser.expect.element('.gb-pages__ellipsis:nth-child(1)').to.be.visible.before(1000);
    browser.expect.element('.gb-pages__ellipsis:nth-child(7)').to.be.visible;
    browser.end();
  }
};
<commit_msg>fix e2e tests<commit_after>module.exports = {
  'initial state': (browser) => {
    browser.url('http://localhost:9090')
      .waitForRiot()
      .expect.element('.gb-pager__link.prev.disabled').to.be.present;
    browser.expect.element('.gb-terminal__link.first.disabled').to.be.present;
    browser.expect.element('.gb-terminal__link.last:not(.disabled)').to.be.present;
    browser.expect.element('.gb-pager__link.next:not(.disabled)').to.be.present;
    browser.expect.element('.gb-pages__page.selected').text.to.eq('1');
    browser.expect.element('.gb-pages li:nth-of-type(1) .gb-pages__page').text.to.eq('1');
    browser.expect.element('.gb-pages li:nth-of-type(5) .gb-pages__page').text.to.eq('5');
    browser.elements('css selector', '.gb-pages__page', (result) => {
      browser.assert.equal(result.value.length, 5);
    });
    browser.expect.element('.gb-pages__ellipsis:nth-child(1)').to.not.be.present;
    browser.expect.element('.gb-pages__ellipsis:nth-child(6)').to.be.visible;
    browser.end();
  },

  'go to next page': (browser) => {
    browser.url('http://localhost:9090')
      .waitForRiot()
      .click('.gb-pager__link.next')
      .expect.element('.gb-pager__link.prev:not(.disabled)').to.be.present.before(1000);
    browser.expect.element('.gb-terminal__link.first:not(.disabled)').to.be.present;
    browser.expect.element('.gb-pages__page.selected').text.to.eq('2');
    browser.end();
  },

  'go to previous page': '' + function(browser) {
    browser.url('http://localhost:9090')
      .waitForRiot()
      .click('.gb-pager__link.prev')
      .expect.element('.gb-pager__link.prev.disabled').to.be.present.before(1000);
    browser.expect.element('.gb-terminal__link.first.disabled').to.be.present;
    browser.end();
  },

  'go to first page': '' + function(browser) {
    browser.url('http://localhost:9090')
      .waitForRiot()
      .click('.gb-terminal__link.first')
      .expect.element('.gb-pager__link.prev.disabled').to.be.present.before(1000);
    browser.expect.element('.gb-terminal__link.first.disabled').to.be.present;
    browser.end();
  },

  'go to last page': (browser) => {
    browser.url('http://localhost:9090')
      .waitForRiot()
      .click('.gb-terminal__link.last')
      .expect.element('.gb-pager__link.next.disabled').to.be.present.before(1000);
    browser.expect.element('.gb-terminal__link.last.disabled').to.be.present;
    browser.expect.element('.gb-pages__page.selected').text.to.eq('1000');
    browser.expect.element('.gb-pages__ellipsis:nth-child(1)').to.be.visible;
    browser.expect.element('.gb-pages__ellipsis:nth-child(6)').to.not.be.present;
    browser.end();
  },

  'jump to page': (browser) => {
    browser.url('http://localhost:9090')
      .waitForRiot()
      .click('.gb-pages li:nth-of-type(4) .gb-pages__page')
      .expect.element('.gb-pages__page.selected').text.to.eq('4');
    browser.expect.element('.gb-pages__ellipsis:nth-child(1)').to.be.visible.before(1000);
    browser.expect.element('.gb-pages__ellipsis:nth-child(7)').to.be.visible;
    browser.end();
  }
};
<|endoftext|>


<commit_before>#!/usr/bin/env node

'use strict';

var Promise = require('bluebird'),
    mongoose = require('mongoose'),
    config = require('../lib/config'),
    workers = require('../lib/workers');

mongoose.Promise = Promise;

Promise.coroutine(function* () {
    yield config.load();
    var resources = [
            {res: 'Photo', path: '/photos', model: require('../models/photo').Photo}
        ],
        dburl = 'mongodb://' +
            config.get.mongodb.host + ':' +
            config.get.mongodb.port + '/' +
            config.get.mongodb.dbname;

    mongoose.connect(dburl);
    mongoose.model('Photo', require('../models/photo').Photo);

    console.log('connecting to ' + dburl);

    return Promise.resolve(mongoose.model('Photo').find({}))
        .map(function (photo) {
            return Promise.promisify(function (cb) {
                    gm('../assets/photos/' + photo.uuid)
                        .resize('200', '200', '^>')
                        .gravity('Center')
                        .crop('200', '200')
                        .write('../assets/photos/' + photo.uuid + '-thumb', function (err) {
                            cb(err);
                        });
                })();
        }, {concurrency: 1});
})();<commit_msg>remove queue from thumb script<commit_after>#!/usr/bin/env node

'use strict';

var Promise = require('bluebird'),
    mongoose = require('mongoose'),
    config = require('../lib/config'),
    gm = require('gm'),
    path = require('path');

mongoose.Promise = Promise;

Promise.coroutine(function* () {
    yield config.load();
    var resources = [
            {res: 'Photo', path: '/photos', model: require('../models/photo').Photo}
        ],
        dburl = 'mongodb://' +
            config.get.mongodb.host + ':' +
            config.get.mongodb.port + '/' +
            config.get.mongodb.dbname;

    mongoose.connect(dburl);
    mongoose.model('Photo', require('../models/photo').Photo);

    console.log('connecting to ' + dburl);

    return Promise.resolve(mongoose.model('Photo').find({}))
        .map(function (photo) {
            return Promise.promisify(function (cb) {
                    gm(path.resolve('../assets/photos/' + photo.uuid))
                        .resize('200', '200', '^>')
                        .gravity('Center')
                        .crop('200', '200')
                        .write(path.resolve('../assets/photos/' + photo.uuid + '-thumb'), function (err) {
                            cb(err);
                        });
                })()
                .catch(function (err) {
                    console.log(err.message);
                });
        }, {concurrency: 1});
})();<|endoftext|>


<commit_before>/* jshint expr:true */
var vanilliLogLevel = "error",
    vanilli = require('../../lib/vanilli.js'),
    helper = require('./e2e-helper.js'),
    chai = require('chai'),
    expect = chai.expect;

chai.use(require('chai-http'));

describe('Vanilli', function () {
    var apiPort, fakePort;

    before(function (done) {
        helper.assignPorts(
            function (port) {
                apiPort = port;
            }, function (port) {
                fakePort = port;
            })
            .then(done);
    });

    describe('initialisation', function () {
        it('MUST throw an error if the fake port is not explicitly specified', function () {
            expect(function () {
                vanilli.startVanilli({ apiPort: 1234, logLevel: vanilliLogLevel });
            })
                .to.throw(/Fake server port must be specified/);
        });

        it('MUST throw an error if the api port is not explicitly specified', function () {
            expect(function () {
                vanilli.startVanilli({ fakePort: 1234, logLevel: vanilliLogLevel });
            })
                .to.throw(/API server port must be specified/);
        });
    });

    describe('API', function () {
        var apiClient, vanilliEnvironment;

        beforeEach(function () {
            vanilliEnvironment = vanilli.startVanilli({ apiPort: apiPort, fakePort: fakePort, logLevel: vanilliLogLevel });
            apiClient = chai.request(vanilliEnvironment.apiServer.url);
        });

        afterEach(function () {
            vanilliEnvironment.stop();
        });

        it('MUST be pingable', function (done) {
            apiClient.get('/ping')
                .res(function (res) {
                    expect(res).to.be.json;
                    expect(res.status).to.equal(200);
                    expect(res.body.ping).to.equal("pong");
                    done();
                });
        });

        it('MUST include CORS headers in responses', function (done) {
            apiClient.options('/expect')
                .res(function (res) {
                    console.log(res.header);

                    expect(res.status).to.be.equal(200);
                    expect(res.header['access-control-allow-origin']).to.equal("*");
                    expect(res.header['access-control-allow-methods']).to.deep.equal("OPTIONS, DELETE, POST");
                    expect(res.header['access-control-allow-headers']).to.exist;
                    done();
                });
        });
    });
});
<commit_msg>Removed rogue log.<commit_after>/* jshint expr:true */
var vanilliLogLevel = "error",
    vanilli = require('../../lib/vanilli.js'),
    helper = require('./e2e-helper.js'),
    chai = require('chai'),
    expect = chai.expect;

chai.use(require('chai-http'));

describe('Vanilli', function () {
    var apiPort, fakePort;

    before(function (done) {
        helper.assignPorts(
            function (port) {
                apiPort = port;
            }, function (port) {
                fakePort = port;
            })
            .then(done);
    });

    describe('initialisation', function () {
        it('MUST throw an error if the fake port is not explicitly specified', function () {
            expect(function () {
                vanilli.startVanilli({ apiPort: 1234, logLevel: vanilliLogLevel });
            })
                .to.throw(/Fake server port must be specified/);
        });

        it('MUST throw an error if the api port is not explicitly specified', function () {
            expect(function () {
                vanilli.startVanilli({ fakePort: 1234, logLevel: vanilliLogLevel });
            })
                .to.throw(/API server port must be specified/);
        });
    });

    describe('API', function () {
        var apiClient, vanilliEnvironment;

        beforeEach(function () {
            vanilliEnvironment = vanilli.startVanilli({ apiPort: apiPort, fakePort: fakePort, logLevel: vanilliLogLevel });
            apiClient = chai.request(vanilliEnvironment.apiServer.url);
        });

        afterEach(function () {
            vanilliEnvironment.stop();
        });

        it('MUST be pingable', function (done) {
            apiClient.get('/ping')
                .res(function (res) {
                    expect(res).to.be.json;
                    expect(res.status).to.equal(200);
                    expect(res.body.ping).to.equal("pong");
                    done();
                });
        });

        it('MUST include CORS headers in responses', function (done) {
            apiClient.options('/expect')
                .res(function (res) {
                    expect(res.status).to.be.equal(200);
                    expect(res.header['access-control-allow-origin']).to.equal("*");
                    expect(res.header['access-control-allow-methods']).to.deep.equal("OPTIONS, DELETE, POST");
                    expect(res.header['access-control-allow-headers']).to.exist;
                    done();
                });
        });
    });
});
<|endoftext|>


<commit_before>console.log('TAP Version 13')
console.log('ok - 1 and exit 0')
console.log('1..1')
<commit_msg>minor typo<commit_after>console.log('TAP version 13')
console.log('ok - 1 and exit 0')
console.log('1..1')
<|endoftext|>


<commit_before>const assert = require('assert')
const NakoCompiler = require('../src/nako3')

describe('PluginSystem test', () => {
  const nako = new NakoCompiler()
  // nako.debug = true;
  const cmp = (code, res) => {
    if (nako.debug) {
      console.log('code=' + code)
    }
    assert.equal(nako.runReset(code).log, res)
  }
  // --- test ---
  it('ナデシコエンジンを表示', () => {
    cmp('ナデシコエンジンを表示', 'nadesi.com/v3')
  })
  it('四則演算', () => {
    cmp('1に2を足して3を掛けて表示', '9')
    cmp('10を2で割って表示', '5')
    cmp('10を2で割った余り;それを表示', '0')
  })
  it('JS実行', () => {
    cmp('「3+6」をJS実行して表示', '9')
    cmp('「Math.floor(3.5)」をJS実行して表示', '3')
  })
  it('型変換', () => {
    cmp('「3.14」を文字列変換して表示', '3.14')
    cmp('「0xFF」を整数変換して表示', '255')
  })
  it('変数型確認', () => {
    cmp('30の変数型確認して表示。', 'number')
  })
  it('SIN/COS/TAN', () => {
    cmp('SIN(1)を表示。', Math.sin(1))
    cmp('COS(1)を表示。', Math.cos(1))
    cmp('TAN(1)を表示。', Math.tan(1))
  })
  it('RGB', () => {
    cmp('RGB(255,255,0)を表示。', '#ffff00')
  })
  it('LOGN', () => {
    cmp('LOGN(10,10)を表示。', Math.LOG10E * Math.log(10))
    cmp('LOGN(2,10)を表示。', Math.LOG2E * Math.log(10))
  })
  it('文字挿入', () => {
    cmp('「12345」の2に「**」を文字挿入して表示', '1**2345')
    cmp('「12345」の1に「**」を文字挿入して表示', '**12345')
    cmp('「12345」の6に「**」を文字挿入して表示', '12345**')
    cmp('「12345」の0に「**」を文字挿入して表示', '**12345')
  })
  it('出現回数', () => {
    cmp('「aabbccaabbcc」で「aa」の出現回数。表示', '2')
    cmp('「aa**bb**cc」で「**」の出現回数。表示', '2')
    cmp('「aa.+bb.+cc」で「.+」の出現回数。表示', '2')
  })
  it('シングル文字列', () => {
    cmp('\'abcd\'を表示。', 'abcd')
  })
  it('文字抜き出す', () => {
    cmp('MID(\'abcdef\',1,2)を表示', 'ab')
    cmp('「abcd」の1文字目から2文字を文字抜き出しを表示', 'ab')
    cmp('MID(\'abcdefg\',3,2)を表示', 'cd')
    cmp('「abcd」の3文字目から2文字を文字抜き出しを表示', 'cd')
    cmp('MID(\'abcd\',4,2)を表示', 'd')
    cmp('「abcd」の4文字目から2文字を文字抜き出しを表示', 'd')
  })
  it('RIGHT', () => {
    cmp('RIGHT(\'abcdef\',2)を表示', 'ef')
    cmp('「abcde」の3だけ文字右部分。それを表示', 'cde')
  })
  it('LEFT', () => {
    cmp('LEFT(\'abcd\',2)を表示', 'ab')
    cmp('「abcde」の3だけ文字左部分。それを表示', 'abc')
  })
  it('切り取る', () => {
    cmp('「abc,def,ghi」から「,」まで切り取る。それを表示。', 'abc')
    cmp('「a,b,c」から「*」まで切り取る。それを表示。', 'a,b,c')
  })
  it('文字削除', () => {
    cmp('「abcd」の1から2だけ文字削除。それを表示。', 'cd')
    cmp('「abcd」の2から2だけ文字削除。それを表示。', 'ad')
  })
  it('置換', () => {
    cmp('「a,b,c」の「,」を「-」に置換して表示。', 'a-b-c')
    cmp('「e,f,g」の「,」を「-」へ単置換して表示。', 'e-f,g')
  })
  it('空白除去', () => {
    cmp('「  aaa   」の空白除去して表示。', 'aaa')
  })
  it('正規表現置換', () => {
    cmp('「aa,bb,cc」の「[a-z]+」を「x」で正規表現置換して表示。', 'x,x,x')
    cmp('「aa,bb,cc」の「/[a-z]+/」を「x」で正規表現置換して表示。', 'x,bb,cc')
    cmp('「aa,bb,cc」の「/[a-z]+/g」を「x」で正規表現置換して表示。', 'x,x,x')
  })
  it('正規表現マッチ - /.../を省略', () => {
    // パターンを省略するとグローバルマッチ
    cmp('「aa,bb,cc」を「[a-z]+」で正規表現マッチ。JSONエンコード。表示。', '["aa","bb","cc"]')
    // グループを指定しても、結果は無視
    cmp('「aa,bb,cc」を「([a-z]+)」で正規表現マッチ。JSONエンコード。表示。', '["aa","bb","cc"]')
  })
  it('正規表現マッチ - /.../あり グルーピングなし', () => {
    cmp('「12-34-56」を「/[0-9]+\\-/」で正規表現マッチ。JSONエンコード。表示。', '"12-"')
  })
  it('正規表現マッチ - /.../あり グルーピングあり', () => {
    // グループ(..)を指定した場合
    cmp('「12-34-56」を「/([0-9]+)\\-/」で正規表現マッチ。JSONエンコード。表示。抽出文字列をJSONエンコードして表示。', '"12-"\n["12"]')
  })
  it('正規表現マッチ2', () => {
    cmp('「AA,BB,CC」を「/^[a-z]+/i」で正規表現マッチ。表示。', 'AA')
  })
  it('正規表現区切る', () => {
    cmp('「aa,bb,cc」を「/\\,/g」で正規表現区切る。JSONエンコード。表示。', '["aa","bb","cc"]')
  })
  it('通貨形式', () => {
    cmp('12345を通貨形式。表示。', '12,345')
    cmp('1000を通貨形式。表示。', '1,000')
  })
  it('ゼロ埋め', () => {
    cmp('10を3でゼロ埋め。表示。', '010')
    cmp('123を5でゼロ埋め。表示。', '00123')
    cmp('12345を3でゼロ埋め。表示。', '12345')
  })
  it('配列要素数', () => {
    cmp('A=[0,1,2,3];Aの配列要素数。表示。', '4')
    cmp('A={"a":1,"b":2,"c":3};Aの配列要素数。表示。', '3')
  })
  it('配列一括挿入', () => {
    cmp('A=[1,1,1];Aの1に[0,0]を配列一括挿入。JSONエンコード。表示。', '[1,0,0,1,1]')
  })
  it('配列ソート', () => {
    cmp('A=[\'ccc\',\'bb\',\'aaa\'];Aを配列ソート。Aを「:」で配列結合。表示。', 'aaa:bb:ccc')
  })
  it('配列数値ソート', () => {
    cmp('A=[\'a\',1,3,2];Aを配列数値ソート。Aを「:」で配列結合。表示。', 'a:1:2:3')
    cmp('A=[\'30\',\'222\',\'55\'];Aを配列数値ソート。Aを「:」で配列結合。表示。', '30:55:222')
  })
  it('配列カスタムソート', () => {
    cmp('●HOGE(aをbで)\n(b-a)を戻す\n---\n' +
      'A=[1,5,3];Aを「HOGE」で配列カスタムソート。Aを「:」で配列結合。表示。', '5:3:1')
  })
  it('配列逆順', () => {
    cmp('A=[1,2,3];Aを配列逆順。Aを「:」で配列結合。表示。', '3:2:1')
  })
  it('配列切り取', () => {
    cmp('A=[0,1,2,3];Aの2を配列切り取る。C=それ。Aを「:」で配列結合。表示。Cを表示', '0:1:3\n2')
  })
  it('日時', () => {
    cmp('「2017/03/06」の曜日。それを表示', '月')
    cmp('「2017/03/06 00:00:00」をUNIXTIME変換して表示', '1488726000')
    cmp('「2017/03/06 00:00:01」をUNIXTIME変換して表示', '1488726001')
    cmp('1488726000を日時変換して表示', '2017/03/06 00:00:00')
  })
  it('文字種変換', () => {
    cmp('「abc」を大文字変換して表示', 'ABC')
    cmp('「ABC」を小文字変換して表示', 'abc')
    cmp('「アイウ」を平仮名変換して表示', 'あいう')
    cmp('「あいう」をカタカナ変換して表示', 'アイウ')
  })
  it('空配列', () => {
    cmp('A=空配列;A@0=10;A@1=20;A@2=30;A@1を表示。', '20')
  })
  it('四捨五入', () => {
    cmp('3.14を四捨五入して表示。', '3')
    cmp('3.6を四捨五入して表示。', '4')
    cmp('3.5を四捨五入して表示。', '4')
  })
  it('切り上げ・切り捨て', () => {
    cmp('3.14を切り上げして表示。', '4')
    cmp('3.8を切り上げして表示。', '4')
    cmp('3.1を切り捨てして表示。', '3')
    cmp('3.8を切り捨てして表示。', '3')
  })
})
<commit_msg>文字抜き出し命令のテストをMID命令に合わせて修正<commit_after>const assert = require('assert')
const NakoCompiler = require('../src/nako3')

describe('PluginSystem test', () => {
  const nako = new NakoCompiler()
  // nako.debug = true;
  const cmp = (code, res) => {
    if (nako.debug) {
      console.log('code=' + code)
    }
    assert.equal(nako.runReset(code).log, res)
  }
  // --- test ---
  it('ナデシコエンジンを表示', () => {
    cmp('ナデシコエンジンを表示', 'nadesi.com/v3')
  })
  it('四則演算', () => {
    cmp('1に2を足して3を掛けて表示', '9')
    cmp('10を2で割って表示', '5')
    cmp('10を2で割った余り;それを表示', '0')
  })
  it('JS実行', () => {
    cmp('「3+6」をJS実行して表示', '9')
    cmp('「Math.floor(3.5)」をJS実行して表示', '3')
  })
  it('型変換', () => {
    cmp('「3.14」を文字列変換して表示', '3.14')
    cmp('「0xFF」を整数変換して表示', '255')
  })
  it('変数型確認', () => {
    cmp('30の変数型確認して表示。', 'number')
  })
  it('SIN/COS/TAN', () => {
    cmp('SIN(1)を表示。', Math.sin(1))
    cmp('COS(1)を表示。', Math.cos(1))
    cmp('TAN(1)を表示。', Math.tan(1))
  })
  it('RGB', () => {
    cmp('RGB(255,255,0)を表示。', '#ffff00')
  })
  it('LOGN', () => {
    cmp('LOGN(10,10)を表示。', Math.LOG10E * Math.log(10))
    cmp('LOGN(2,10)を表示。', Math.LOG2E * Math.log(10))
  })
  it('文字挿入', () => {
    cmp('「12345」の2に「**」を文字挿入して表示', '1**2345')
    cmp('「12345」の1に「**」を文字挿入して表示', '**12345')
    cmp('「12345」の6に「**」を文字挿入して表示', '12345**')
    cmp('「12345」の0に「**」を文字挿入して表示', '**12345')
  })
  it('出現回数', () => {
    cmp('「aabbccaabbcc」で「aa」の出現回数。表示', '2')
    cmp('「aa**bb**cc」で「**」の出現回数。表示', '2')
    cmp('「aa.+bb.+cc」で「.+」の出現回数。表示', '2')
  })
  it('シングル文字列', () => {
    cmp('\'abcd\'を表示。', 'abcd')
  })
  it('文字抜き出す', () => {
    cmp('MID(\'abcdef\',1,2)を表示', 'ab')
    cmp('「abcdef」の1文字目から2文字を文字抜き出しを表示', 'ab')
    cmp('MID(\'abcdefg\',3,2)を表示', 'cd')
    cmp('「abcdefg」の3文字目から2文字を文字抜き出しを表示', 'cd')
    cmp('MID(\'abcd\',4,2)を表示', 'd')
    cmp('「abcd」の4文字目から2文字を文字抜き出しを表示', 'd')
  })
  it('RIGHT', () => {
    cmp('RIGHT(\'abcdef\',2)を表示', 'ef')
    cmp('「abcde」の3だけ文字右部分。それを表示', 'cde')
  })
  it('LEFT', () => {
    cmp('LEFT(\'abcd\',2)を表示', 'ab')
    cmp('「abcde」の3だけ文字左部分。それを表示', 'abc')
  })
  it('切り取る', () => {
    cmp('「abc,def,ghi」から「,」まで切り取る。それを表示。', 'abc')
    cmp('「a,b,c」から「*」まで切り取る。それを表示。', 'a,b,c')
  })
  it('文字削除', () => {
    cmp('「abcd」の1から2だけ文字削除。それを表示。', 'cd')
    cmp('「abcd」の2から2だけ文字削除。それを表示。', 'ad')
  })
  it('置換', () => {
    cmp('「a,b,c」の「,」を「-」に置換して表示。', 'a-b-c')
    cmp('「e,f,g」の「,」を「-」へ単置換して表示。', 'e-f,g')
  })
  it('空白除去', () => {
    cmp('「  aaa   」の空白除去して表示。', 'aaa')
  })
  it('正規表現置換', () => {
    cmp('「aa,bb,cc」の「[a-z]+」を「x」で正規表現置換して表示。', 'x,x,x')
    cmp('「aa,bb,cc」の「/[a-z]+/」を「x」で正規表現置換して表示。', 'x,bb,cc')
    cmp('「aa,bb,cc」の「/[a-z]+/g」を「x」で正規表現置換して表示。', 'x,x,x')
  })
  it('正規表現マッチ - /.../を省略', () => {
    // パターンを省略するとグローバルマッチ
    cmp('「aa,bb,cc」を「[a-z]+」で正規表現マッチ。JSONエンコード。表示。', '["aa","bb","cc"]')
    // グループを指定しても、結果は無視
    cmp('「aa,bb,cc」を「([a-z]+)」で正規表現マッチ。JSONエンコード。表示。', '["aa","bb","cc"]')
  })
  it('正規表現マッチ - /.../あり グルーピングなし', () => {
    cmp('「12-34-56」を「/[0-9]+\\-/」で正規表現マッチ。JSONエンコード。表示。', '"12-"')
  })
  it('正規表現マッチ - /.../あり グルーピングあり', () => {
    // グループ(..)を指定した場合
    cmp('「12-34-56」を「/([0-9]+)\\-/」で正規表現マッチ。JSONエンコード。表示。抽出文字列をJSONエンコードして表示。', '"12-"\n["12"]')
  })
  it('正規表現マッチ2', () => {
    cmp('「AA,BB,CC」を「/^[a-z]+/i」で正規表現マッチ。表示。', 'AA')
  })
  it('正規表現区切る', () => {
    cmp('「aa,bb,cc」を「/\\,/g」で正規表現区切る。JSONエンコード。表示。', '["aa","bb","cc"]')
  })
  it('通貨形式', () => {
    cmp('12345を通貨形式。表示。', '12,345')
    cmp('1000を通貨形式。表示。', '1,000')
  })
  it('ゼロ埋め', () => {
    cmp('10を3でゼロ埋め。表示。', '010')
    cmp('123を5でゼロ埋め。表示。', '00123')
    cmp('12345を3でゼロ埋め。表示。', '12345')
  })
  it('配列要素数', () => {
    cmp('A=[0,1,2,3];Aの配列要素数。表示。', '4')
    cmp('A={"a":1,"b":2,"c":3};Aの配列要素数。表示。', '3')
  })
  it('配列一括挿入', () => {
    cmp('A=[1,1,1];Aの1に[0,0]を配列一括挿入。JSONエンコード。表示。', '[1,0,0,1,1]')
  })
  it('配列ソート', () => {
    cmp('A=[\'ccc\',\'bb\',\'aaa\'];Aを配列ソート。Aを「:」で配列結合。表示。', 'aaa:bb:ccc')
  })
  it('配列数値ソート', () => {
    cmp('A=[\'a\',1,3,2];Aを配列数値ソート。Aを「:」で配列結合。表示。', 'a:1:2:3')
    cmp('A=[\'30\',\'222\',\'55\'];Aを配列数値ソート。Aを「:」で配列結合。表示。', '30:55:222')
  })
  it('配列カスタムソート', () => {
    cmp('●HOGE(aをbで)\n(b-a)を戻す\n---\n' +
      'A=[1,5,3];Aを「HOGE」で配列カスタムソート。Aを「:」で配列結合。表示。', '5:3:1')
  })
  it('配列逆順', () => {
    cmp('A=[1,2,3];Aを配列逆順。Aを「:」で配列結合。表示。', '3:2:1')
  })
  it('配列切り取', () => {
    cmp('A=[0,1,2,3];Aの2を配列切り取る。C=それ。Aを「:」で配列結合。表示。Cを表示', '0:1:3\n2')
  })
  it('日時', () => {
    cmp('「2017/03/06」の曜日。それを表示', '月')
    cmp('「2017/03/06 00:00:00」をUNIXTIME変換して表示', '1488726000')
    cmp('「2017/03/06 00:00:01」をUNIXTIME変換して表示', '1488726001')
    cmp('1488726000を日時変換して表示', '2017/03/06 00:00:00')
  })
  it('文字種変換', () => {
    cmp('「abc」を大文字変換して表示', 'ABC')
    cmp('「ABC」を小文字変換して表示', 'abc')
    cmp('「アイウ」を平仮名変換して表示', 'あいう')
    cmp('「あいう」をカタカナ変換して表示', 'アイウ')
  })
  it('空配列', () => {
    cmp('A=空配列;A@0=10;A@1=20;A@2=30;A@1を表示。', '20')
  })
  it('四捨五入', () => {
    cmp('3.14を四捨五入して表示。', '3')
    cmp('3.6を四捨五入して表示。', '4')
    cmp('3.5を四捨五入して表示。', '4')
  })
  it('切り上げ・切り捨て', () => {
    cmp('3.14を切り上げして表示。', '4')
    cmp('3.8を切り上げして表示。', '4')
    cmp('3.1を切り捨てして表示。', '3')
    cmp('3.8を切り捨てして表示。', '3')
  })
})
<|endoftext|>


<commit_before>//remove:
require('../build/function-calling.node.js');
//:remove

wru.test([
  {
    name: 'main',
    test: function () {
      wru.assert(typeof Function.calling == "function");
    }
  },{
    name: 'used like bind',
    test: function () {
      var o = {};
      wru.async(function(){
        wru.assert('right context', this === o);
      }.calling(o))();
      wru.async(function(a){
        wru.assert('right args', this === o && a === 1);
      }.calling(o, [1]))();
      wru.async(function(a, b){
        wru.assert('right multiple args', this === o && a === 1 && b === 2);
      }.calling(o, [1, 2]))();
    }
  },{
    name: 'limiting arguments',
    test: function () {
      var pInt = parseInt.calling(undefined, [null, 10]);
      wru.assert('still base 10', pInt('08', 2) === 8);
      wru.assert('usable with Arrays', ['01', '02'].map(pInt).join(',') === '1,2');
      wru.assert('not usable with Arrays', pInt('0a', 16) === 0);
    }
  },{
    name: 'not limiting arguments',
    test: function () {
      var pInt = parseInt.calling(undefined, [null], true),
          fromCharCode  = String.fromCharCode.calling(String, [], true),
          charCodeAt    = String.prototype.charCodeAt.calling(null, [0]);
      wru.log('second argument cnsidered', pInt('0a', 16) === 10);
      wru.log('generic function', fromCharCode(1, 2, 3) === [1, 2, 3].map(charCodeAt).join(''));
    }
  },{
    name: 'hybrid',
    test: function () {
      var hybrid = [].join.calling(
        null,
        ['1', null, '2', undefined, '3', null, '4']
      );
      wru.log('' + hybrid)
    }
  }
]);
<commit_msg>moar tests<commit_after>//remove:
require('../build/function-calling.node.js');
//:remove

wru.test([
  {
    name: 'main',
    test: function () {
      wru.assert(typeof Function.calling == "function");
    }
  },{
    name: 'used like bind',
    test: function () {
      var o = {};
      wru.async(function(){
        wru.assert('right context', this === o);
      }.calling(o))();
      wru.async(function(a){
        wru.assert('right args', this === o && a === 1);
      }.calling(o, [1]))();
      wru.async(function(a, b){
        wru.assert('right multiple args', this === o && a === 1 && b === 2);
      }.calling(o, [1, 2]))();
    }
  },{
    name: 'limiting arguments',
    test: function () {
      var pInt = parseInt.calling(undefined, [null, 10]);
      wru.assert('still base 10', pInt('08', 2) === 8);
      wru.assert('usable with Arrays', ['01', '02'].map(pInt).join(',') === '1,2');
      wru.assert('not usable with Arrays', pInt('0a', 16) === 0);
    }
  },{
    name: 'not limiting arguments',
    test: function () {
      var pInt = parseInt.calling(undefined, [null], true),
          fromCharCode  = String.fromCharCode.calling(String, [], true),
          charCodeAt    = String.prototype.charCodeAt.calling(null, [0]);
      wru.log('second argument cnsidered', pInt('0a', 16) === 10);
      wru.log('generic function', fromCharCode(1, 2, 3) === [1, 2, 3].map(charCodeAt).join(''));
    }
  },{
    name: 'hybrid',
    test: function () {
      var hybrid = ''.concat.calling(
        '',
        [1, null, 2, undefined, 3, null, 4]
      );
      wru.assert('it works!', ['192','384'].join('' + undefined) === hybrid(9, 8));
    }
  }
]);
<|endoftext|>


<commit_before>'use strict';

var path         = require('path'),
    fs           = require('fs'),
    Q            = require('q'),
    ncp          = require('ncp'),
    defaults     = require('lodash.defaults'),
    flatten      = require('lodash.flatten'),
    template     = require('lodash.template'),
    isArray      = require('lodash.isarray'),
    childProcess = require('child_process');

var platform = require('../../lib/config/platform');

/**
 * Create an instance based with the given parameter defaults.
 * Since this method is not exposed to the user we can rely on base to be correctly formed with array
 * properties.
 * @param {object} [base] Optional default set of parameters to merge
 * @returns {{create: {function}, reset: {function}, seal: {function}, withDirectories: {function},
 *          forProgram: {function}, addSource: {function}, withSourceFilter: {function}, addExpectation: {function},
 *          addInvocation: {function}, run: {function}}} A new instance
 */
function factory(base) {

  // initialise parameters
  var params;
  reset();

  // compose instance
  var self = {
    create          : create,
    toString        : toString,
    reset           : reset,
    seal            : seal,
    withDirectories : withDirectories,
    withTimeout     : withTimeout,
    forProgram      : forProgram,
    addSource       : addSource,
    withSourceFilter: withSourceFilter,
    addExpectation  : addExpectation,
    addInvocation   : addInvocation,
    addParameters   : addParameters,
    toArray         : toArray,
    forEach         : forEach,
    run             : run
  };
  return self;

  /**
   * Create a new instance with a separate set of properties
   * @returns {Object}
   */
  function create() {
    return factory(params);
  }

  /**
   * Infer the command from the first invocation/parameter-set combination
   * @returns {string} The inferred command
   */
  function toString() {
    var invocation = params.invocations[0]   || [];
    var paramSet   = params.parameterSets[0] || {};
    function resolveTemplate(item) {
      return template(item, paramSet, {interpolate: /\{\s*(\w+)\s*\}/});
    }
    return [params.program]
      .concat(invocation.map(resolveTemplate))
      .join(' ');
  }

  /**
   * Reset the instance parameters but leave its defaults unchanged
   * @returns {object} The same instance with mutated properties
   */
  function reset() {

    // default value
    params = {
      directories  : {},
      sources      : [],
      filter       : null,
      timeout      : false,
      program      : null,
      expectations : [],
      invocations  : [],
      parameterSets: [],
      hasRun       : false
    };

    // clone params, must duplicate arrays
    for (var key in base || {}) {
      var value = base[key];
      params[key] = isArray(value) ? value.concat() : value;
    }

    // complete
    return self;
  }

  /**
   * Get a copy of the instance that
   * @returns {{create: create}}
   */
  function seal() {
    return {
      create: create
    };
  }

  /**
   * Set the directories that will be used
   * @param {string} source The directory that contains sources
   * @param {string} temp The directory that will be used as the temp directory
   * @returns {object} The same instance with mutated properties
   */
  function withDirectories(source, temp) {
    params.directories.source = source;
    params.directories.temp   = temp;
    return self;
  }

  /**
   * Set the watchdog timeout that will end the process when elapsed
   * @param {number} [milliseconds] The timeout in milliseconds or omitted for no timeout
   * @returns {object} The same instance with mutated properties
   */
  function withTimeout(milliseconds) {
    params.timeout = (typeof milliseconds === 'number') && Math.max(0, milliseconds);
    return self;
  }

  /**
   * Set the program that will be run
   * @param {string} program The program name
   * @returns {object} The same instance with mutated properties
   */
  function forProgram(program) {
    params.program = program;
    return self;
  }

  /**
   * Add the given source directory
   * @param {string} source The simple name of a directory in the expected folder
   * @returns {object} The same instance with mutated properties
   */
  function addSource(source) {
    params.sources.push(source);
    return self;
  }

  /**
   * Set the filter to control which source files are copied to the working directory
   * @param {RegExp|function} filter A regular expression or a function that tests a given directory path
   * @returns {object} The same instance with mutated properties
   */
  function withSourceFilter(filter) {
    params.filter = filter;
    return self;
  }

  /**
   * Add the given expectation
   * @param {function} expectation A function with jasmine expectations
   * @returns {object} The same instance with mutated properties
   */
  function addExpectation(expectation) {
    params.expectations.push(expectation);
    return self;
  }

  /**
   * Add the given command line argument set as a test case
   * @param {...string} parameters Any number of arguments for the command line
   * @returns {object} The same instance with mutated properties
   */
  function addInvocation() {
    var args = flatten(Array.prototype.slice.call(arguments));
    params.invocations.push(args);
    return self;
  }

  /**
   * Add the given parameter set as a test case
   * @param {object} parameters Values that will substitute into the invocation template
   */
  function addParameters(parameters) {
    params.parameterSets.push(parameters);
    return self;
  }

  /**
   * Enumerate all test cases
   * @param {function} callback The method to call with the instance and the test case
   */
  function toArray() {
    var sources       = params.sources.length       ? params.sources       : [null];
    var parameterSets = params.parameterSets.length ? params.parameterSets : [ {} ];
    var results       = [];
    parameterSets.forEach(function eachParamSet(paramSet) {
      sources.forEach(function eachSource(source) {
        params.invocations.forEach(function eachInvocation(invocation) {

          // create an isolated instance
          var instance = factory(defaults({
            sources      : [source],
            invocations  : [invocation],
            parameterSets: [paramSet]
          }, params));

          // accumulate instances
          results.push(instance);
        });
      });
    });
    return results;
  }

  /**
   * Iterate all the test cases per toArray()
   * @param {function} method The method that each case will be applied to and which should return a promise
   * @return {promise} A promise that resolves when all method promises resolve
   */
  function forEach(method) {
    return Q.all(toArray().map(method));
  }

  /**
   * Execute the parameters encoded in the current instance as a test.
   * If run has occurred before (without reset) then the source will not be re-copied to the working folder.
   * @returns {{then: {function}, catch: {function}, finally: {function}} A promise that resolves when tests complete
   */
  function run() {

    // if we can get a list of single instances we recurse them
    var list = toArray();
    if (list.length > 1) {
      var promises = list
        .map(function(instance) {
          return instance.run();
        });
      return Q.all(promises);
    }
    // otherwise single instance
    else {
      return runSingle();
    }
  }

  /**
   * Run the first permutation of sources and parameter sets
   * @returns {{then: {function}, catch: {function}, finally: {function}, notify: {function}} A promise that resolves
   *          when test completes and notifies when buffers are updated
   */
  function runSingle() {
    var resolveSrc  = ensureDirectory(params.directories.source);
    var resolveDest = ensureDirectory(params.directories.temp);
    var source      = params.sources[0];
    var paramSet    = params.parameterSets[0];
    var stdio       = {stdout: '', stderr: ''};
    var child;
    var timeout;

    // each combination is async
    var deferred = Q.defer();

    // determine the overall command we will run
    var command = toString();

    // organise a working directory
    var signature = escapeFilenameString(source, command);
    var src       = !params.hasRun && resolveSrc(source);
    var cwd       = resolveDest(signature);
    copySources(src, cwd, onCopyComplete);

    // handler for end of copy process
    function onCopyComplete(error) {

      // error in copying implies rejected async
      if (error) {
        deferred.reject(error);
      }
      // run the command
      else {

        // spawn a child process in an exec()-like manner
        //  https://github.com/joyent/node/issues/2318#issuecomment-32706753
        var args = platform.isWindows() ?
          ['cmd.exe', ['/s', '/c', '"' + command + '"']] :
          ['/bin/sh', ['-c', command]];
        child = childProcess.spawn.apply(childProcess, args.concat({
          cwd                     : cwd,
          stdio                   : 'pipe',
          windowsVerbatimArguments: true
        }));

        // add listeners to the child process
        notifyOn('stdout');
        notifyOn('stderr');
        child.on('close', onClose);

        // start the watchdog timer
        useTimeout(true);
      }
    }

    // clear the watchdog timeout and optionally start another
    function useTimeout(isStart) {
      if (timeout) {
        clearTimeout(timeout);
      }
      if (isStart) {
        timeout = setTimeout(kill, params.timeout || 1E+9);
      }
    }

    // progress on stdio
    function notifyOn(name) {
      var stream = child[name];
      stream.on('readable', function onReadable() {
        var chunk = stream.read();
        if (chunk !== null) {

          // cancel watchdog timeout
          useTimeout(true);

          // append the chunk to the stream text
          stdio[name] = (stdio[name] || '') + chunk.toString();

          // progress the promise
          var testCase = getTestCase({kill: kill}, paramSet);
          deferred.notify(testCase);
        }
      });
    }

    // async resolution on process complete or timeout
    function onClose(exitcode) {
console.log('!!!!ONCLOSE!!!!');
      process.nextTick(function nextTickResolve() {
console.log('nextTick() resolve');

        // cancel watchdog timeout and mark as run
        useTimeout(false);
        params.hasRun = true;

        // resolve the promise
        var testCase = getTestCase({exitcode: exitcode}, paramSet);
        deferred.resolve(testCase);
      });
    }

    // the test case is the merge of the test stats and the given arguments, usually the parameter set
    function getTestCase() {
      var args      = Array.prototype.slice.call(arguments);
      var testStats = {
        sourceDir: src,
        runner   : self,
        cwd      : cwd,
        command  : command,
        stdout   : stdio.stdout,
        stderr   : stdio.stderr
      };
      return defaults.apply(null, [testStats].concat(args));
    }

    // kill the child process
    function kill() {
      child.kill();
    }

    // async until process completes
    return deferred.promise;
  }

  /**
   * Use ncp to copy files
   * @param {string|null} src The source directory
   * @param {string} dest The destination directory
   * @param {function} callback Completion callback
   */
  function copySources(src, dest, callback) {
    if (src) {
      ncp(src, dest, {
        filter: params.filter || /.*/
      }, callback);
    } else {
      ensureDirectory(dest);
      callback();
    }
  }
}

/**
 * Flatten the given parameters as a single string suitable for file names
 * @param {...string|Array} Any number of strings or arrays thereof
 * @returns {string} A dot delimited string
 */
function escapeFilenameString() {
  return flatten(Array.prototype.slice.call(arguments))
    .filter(Boolean)
    .join('.')
    .replace(/["']/g, '')                                                                     // omit quotes
    .replace(/[^ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._-]+/g, '.');  // change illegal chars
}

/**
 * Ensure that that given directory exists relative to the project directory
 * @param {...string} elements Any number of path elements relative to the project directory
 * @returns {function} A resolver that encapsulates the given directory
 */
function ensureDirectory() {

  // get a method that will path.resolve() against a base directory
  function getResolver(base) {
    return function() {
      var args = flatten(Array.prototype.slice.call(arguments));
      if (args.every(Boolean)) {
        return path.resolve.apply(path, [base].concat(args));
      } else {
        return null;
      }
    };
  }

  // find the project directory based on the package json
  var projectDir = __dirname;
  while (!fs.existsSync(path.join(projectDir, 'package.json'))) {
    projectDir = path.resolve(projectDir, '..');
  }

  // resolve the given directory path and make the directory if it does not exist
  var args     = Array.prototype.slice.call(arguments);
  var resolved = getResolver(projectDir)(args);
  if (!fs.existsSync(resolved)) {
    fs.mkdirSync(resolved);
  }

  // return the resolver
  return getResolver(resolved);
}

module.exports = {
  create: function() {
    return factory();
  }
};<commit_msg>attempting to debug CI problem not seen locally (try setImmediate in place of nextTick)<commit_after>'use strict';

var path         = require('path'),
    fs           = require('fs'),
    Q            = require('q'),
    ncp          = require('ncp'),
    defaults     = require('lodash.defaults'),
    flatten      = require('lodash.flatten'),
    template     = require('lodash.template'),
    isArray      = require('lodash.isarray'),
    childProcess = require('child_process');

var platform = require('../../lib/config/platform');

/**
 * Create an instance based with the given parameter defaults.
 * Since this method is not exposed to the user we can rely on base to be correctly formed with array
 * properties.
 * @param {object} [base] Optional default set of parameters to merge
 * @returns {{create: {function}, reset: {function}, seal: {function}, withDirectories: {function},
 *          forProgram: {function}, addSource: {function}, withSourceFilter: {function}, addExpectation: {function},
 *          addInvocation: {function}, run: {function}}} A new instance
 */
function factory(base) {

  // initialise parameters
  var params;
  reset();

  // compose instance
  var self = {
    create          : create,
    toString        : toString,
    reset           : reset,
    seal            : seal,
    withDirectories : withDirectories,
    withTimeout     : withTimeout,
    forProgram      : forProgram,
    addSource       : addSource,
    withSourceFilter: withSourceFilter,
    addExpectation  : addExpectation,
    addInvocation   : addInvocation,
    addParameters   : addParameters,
    toArray         : toArray,
    forEach         : forEach,
    run             : run
  };
  return self;

  /**
   * Create a new instance with a separate set of properties
   * @returns {Object}
   */
  function create() {
    return factory(params);
  }

  /**
   * Infer the command from the first invocation/parameter-set combination
   * @returns {string} The inferred command
   */
  function toString() {
    var invocation = params.invocations[0]   || [];
    var paramSet   = params.parameterSets[0] || {};
    function resolveTemplate(item) {
      return template(item, paramSet, {interpolate: /\{\s*(\w+)\s*\}/});
    }
    return [params.program]
      .concat(invocation.map(resolveTemplate))
      .join(' ');
  }

  /**
   * Reset the instance parameters but leave its defaults unchanged
   * @returns {object} The same instance with mutated properties
   */
  function reset() {

    // default value
    params = {
      directories  : {},
      sources      : [],
      filter       : null,
      timeout      : false,
      program      : null,
      expectations : [],
      invocations  : [],
      parameterSets: [],
      hasRun       : false
    };

    // clone params, must duplicate arrays
    for (var key in base || {}) {
      var value = base[key];
      params[key] = isArray(value) ? value.concat() : value;
    }

    // complete
    return self;
  }

  /**
   * Get a copy of the instance that
   * @returns {{create: create}}
   */
  function seal() {
    return {
      create: create
    };
  }

  /**
   * Set the directories that will be used
   * @param {string} source The directory that contains sources
   * @param {string} temp The directory that will be used as the temp directory
   * @returns {object} The same instance with mutated properties
   */
  function withDirectories(source, temp) {
    params.directories.source = source;
    params.directories.temp   = temp;
    return self;
  }

  /**
   * Set the watchdog timeout that will end the process when elapsed
   * @param {number} [milliseconds] The timeout in milliseconds or omitted for no timeout
   * @returns {object} The same instance with mutated properties
   */
  function withTimeout(milliseconds) {
    params.timeout = (typeof milliseconds === 'number') && Math.max(0, milliseconds);
    return self;
  }

  /**
   * Set the program that will be run
   * @param {string} program The program name
   * @returns {object} The same instance with mutated properties
   */
  function forProgram(program) {
    params.program = program;
    return self;
  }

  /**
   * Add the given source directory
   * @param {string} source The simple name of a directory in the expected folder
   * @returns {object} The same instance with mutated properties
   */
  function addSource(source) {
    params.sources.push(source);
    return self;
  }

  /**
   * Set the filter to control which source files are copied to the working directory
   * @param {RegExp|function} filter A regular expression or a function that tests a given directory path
   * @returns {object} The same instance with mutated properties
   */
  function withSourceFilter(filter) {
    params.filter = filter;
    return self;
  }

  /**
   * Add the given expectation
   * @param {function} expectation A function with jasmine expectations
   * @returns {object} The same instance with mutated properties
   */
  function addExpectation(expectation) {
    params.expectations.push(expectation);
    return self;
  }

  /**
   * Add the given command line argument set as a test case
   * @param {...string} parameters Any number of arguments for the command line
   * @returns {object} The same instance with mutated properties
   */
  function addInvocation() {
    var args = flatten(Array.prototype.slice.call(arguments));
    params.invocations.push(args);
    return self;
  }

  /**
   * Add the given parameter set as a test case
   * @param {object} parameters Values that will substitute into the invocation template
   */
  function addParameters(parameters) {
    params.parameterSets.push(parameters);
    return self;
  }

  /**
   * Enumerate all test cases
   * @param {function} callback The method to call with the instance and the test case
   */
  function toArray() {
    var sources       = params.sources.length       ? params.sources       : [null];
    var parameterSets = params.parameterSets.length ? params.parameterSets : [ {} ];
    var results       = [];
    parameterSets.forEach(function eachParamSet(paramSet) {
      sources.forEach(function eachSource(source) {
        params.invocations.forEach(function eachInvocation(invocation) {

          // create an isolated instance
          var instance = factory(defaults({
            sources      : [source],
            invocations  : [invocation],
            parameterSets: [paramSet]
          }, params));

          // accumulate instances
          results.push(instance);
        });
      });
    });
    return results;
  }

  /**
   * Iterate all the test cases per toArray()
   * @param {function} method The method that each case will be applied to and which should return a promise
   * @return {promise} A promise that resolves when all method promises resolve
   */
  function forEach(method) {
    return Q.all(toArray().map(method));
  }

  /**
   * Execute the parameters encoded in the current instance as a test.
   * If run has occurred before (without reset) then the source will not be re-copied to the working folder.
   * @returns {{then: {function}, catch: {function}, finally: {function}} A promise that resolves when tests complete
   */
  function run() {

    // if we can get a list of single instances we recurse them
    var list = toArray();
    if (list.length > 1) {
      var promises = list
        .map(function(instance) {
          return instance.run();
        });
      return Q.all(promises);
    }
    // otherwise single instance
    else {
      return runSingle();
    }
  }

  /**
   * Run the first permutation of sources and parameter sets
   * @returns {{then: {function}, catch: {function}, finally: {function}, notify: {function}} A promise that resolves
   *          when test completes and notifies when buffers are updated
   */
  function runSingle() {
    var resolveSrc  = ensureDirectory(params.directories.source);
    var resolveDest = ensureDirectory(params.directories.temp);
    var source      = params.sources[0];
    var paramSet    = params.parameterSets[0];
    var stdio       = {stdout: '', stderr: ''};
    var child;
    var timeout;

    // each combination is async
    var deferred = Q.defer();

    // determine the overall command we will run
    var command = toString();

    // organise a working directory
    var signature = escapeFilenameString(source, command);
    var src       = !params.hasRun && resolveSrc(source);
    var cwd       = resolveDest(signature);
    copySources(src, cwd, onCopyComplete);

    // handler for end of copy process
    function onCopyComplete(error) {

      // error in copying implies rejected async
      if (error) {
        deferred.reject(error);
      }
      // run the command
      else {

        // spawn a child process in an exec()-like manner
        //  https://github.com/joyent/node/issues/2318#issuecomment-32706753
        var args = platform.isWindows() ?
          ['cmd.exe', ['/s', '/c', '"' + command + '"']] :
          ['/bin/sh', ['-c', command]];
        child = childProcess.spawn.apply(childProcess, args.concat({
          cwd                     : cwd,
          stdio                   : 'pipe',
          windowsVerbatimArguments: true
        }));

        // add listeners to the child process
        notifyOn('stdout');
        notifyOn('stderr');
        child.on('close', onClose);

        // start the watchdog timer
        useTimeout(true);
      }
    }

    // clear the watchdog timeout and optionally start another
    function useTimeout(isStart) {
      if (timeout) {
        clearTimeout(timeout);
      }
      if (isStart) {
        timeout = setTimeout(kill, params.timeout || 1E+9);
      }
    }

    // progress on stdio
    function notifyOn(name) {
      var stream = child[name];
      stream.on('readable', function onReadable() {
        var chunk = stream.read();
        if (chunk !== null) {

          // cancel watchdog timeout
          useTimeout(true);

          // append the chunk to the stream text
          stdio[name] = (stdio[name] || '') + chunk.toString();

          // progress the promise
          var testCase = getTestCase({kill: kill}, paramSet);
          deferred.notify(testCase);
        }
      });
    }

    // async resolution on process complete or timeout
    function onClose(exitcode) {
console.log('!!!!ONCLOSE!!!!');
      process.setImmediate(function nextTickResolve() {
console.log('setImmediate() resolve');

        // cancel watchdog timeout and mark as run
        useTimeout(false);
        params.hasRun = true;

        // resolve the promise
        var testCase = getTestCase({exitcode: exitcode}, paramSet);
        deferred.resolve(testCase);
      });
    }

    // the test case is the merge of the test stats and the given arguments, usually the parameter set
    function getTestCase() {
      var args      = Array.prototype.slice.call(arguments);
      var testStats = {
        sourceDir: src,
        runner   : self,
        cwd      : cwd,
        command  : command,
        stdout   : stdio.stdout,
        stderr   : stdio.stderr
      };
      return defaults.apply(null, [testStats].concat(args));
    }

    // kill the child process
    function kill() {
      child.kill();
    }

    // async until process completes
    return deferred.promise;
  }

  /**
   * Use ncp to copy files
   * @param {string|null} src The source directory
   * @param {string} dest The destination directory
   * @param {function} callback Completion callback
   */
  function copySources(src, dest, callback) {
    if (src) {
      ncp(src, dest, {
        filter: params.filter || /.*/
      }, callback);
    } else {
      ensureDirectory(dest);
      callback();
    }
  }
}

/**
 * Flatten the given parameters as a single string suitable for file names
 * @param {...string|Array} Any number of strings or arrays thereof
 * @returns {string} A dot delimited string
 */
function escapeFilenameString() {
  return flatten(Array.prototype.slice.call(arguments))
    .filter(Boolean)
    .join('.')
    .replace(/["']/g, '')                                                                     // omit quotes
    .replace(/[^ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._-]+/g, '.');  // change illegal chars
}

/**
 * Ensure that that given directory exists relative to the project directory
 * @param {...string} elements Any number of path elements relative to the project directory
 * @returns {function} A resolver that encapsulates the given directory
 */
function ensureDirectory() {

  // get a method that will path.resolve() against a base directory
  function getResolver(base) {
    return function() {
      var args = flatten(Array.prototype.slice.call(arguments));
      if (args.every(Boolean)) {
        return path.resolve.apply(path, [base].concat(args));
      } else {
        return null;
      }
    };
  }

  // find the project directory based on the package json
  var projectDir = __dirname;
  while (!fs.existsSync(path.join(projectDir, 'package.json'))) {
    projectDir = path.resolve(projectDir, '..');
  }

  // resolve the given directory path and make the directory if it does not exist
  var args     = Array.prototype.slice.call(arguments);
  var resolved = getResolver(projectDir)(args);
  if (!fs.existsSync(resolved)) {
    fs.mkdirSync(resolved);
  }

  // return the resolver
  return getResolver(resolved);
}

module.exports = {
  create: function() {
    return factory();
  }
};<|endoftext|>


<commit_before>import React from 'react';
import SPELLS from 'common/SPELLS';
import SpellLink from 'common/SpellLink';
import SpellIcon from 'common/SpellIcon';
import { formatPercentage } from 'common/format';
import StatisticBox, { STATISTIC_ORDER } from 'interface/others/StatisticBox';
import Analyzer, { SELECTED_PLAYER } from 'parser/core/Analyzer';
import Events from 'parser/core/Events';
import AbilityTracker from 'parser/shared/modules/AbilityTracker';
import EnemyInstances from 'parser/shared/modules/EnemyInstances';

class Meteor extends Analyzer {
  static dependencies = {
    abilityTracker: AbilityTracker,
    enemies: EnemyInstances,
  };

  badMeteor = 0

  constructor(...args) {
    super(...args);
    this.hasMeteor = this.selectedCombatant.hasTalent(SPELLS.METEOR_TALENT.id);
    this.hasRuneOfPower = this.selectedCombatant.hasTalent(SPELLS.RUNE_OF_POWER_TALENT.id);
    this.active = this.hasMeteor && this.hasRuneOfPower ? true : false;

    if (!this.active) {
      return;
    }

    this.addEventListener(Events.cast.by(SELECTED_PLAYER).spell(SPELLS.METEOR_TALENT), this.onMeteor);
  }

  onMeteor(event) {
    if (this.selectedCombatant.hasBuff(SPELLS.RUNE_OF_POWER_BUFF.id)) {
      this.badMeteor += 1;
    }
  }

  get totalMeteorCasts() {
    return this.abilityTracker.getAbility(SPELLS.METEOR_TALENT.id).casts;
  }

  get meteorUtilization() {
    return 1 - (this.badMeteor / this.totalMeteorCasts);
  }

  get meteorUtilSuggestionThresholds() {
    return {
      actual: this.meteorUtilization,
      isLessThan: {
        minor: 0.95,
        average: 0.90,
        major: 0.85,
      },
      style: 'percentage',
    };
  }

  suggestions(when) {
		when(this.meteorUtilSuggestionThresholds)
			.addSuggestion((suggest, actual, recommended) => {
				return suggest(<>You cast <SpellLink id={SPELLS.METEOR_TALENT.id} /> without <SpellLink id={SPELLS.RUNE_OF_POWER_TALENT.id} /> {this.badMeteor} times. In order to get the most out of <SpellLink id={SPELLS.METEOR_TALENT.id} /> you should always cast it while being buffed by <SpellLink id={SPELLS.RUNE_OF_POWER_TALENT.id} />.</>)
					.icon(SPELLS.METEOR_TALENT.icon)
					.actual(`${formatPercentage(this.meteorUtilization)}% Utilization`)
					.recommended(`<${formatPercentage(recommended)}% is recommended`);
			});
	}

	statistic() {
    return (
      <StatisticBox
        position={STATISTIC_ORDER.CORE(100)}
        icon={<SpellIcon id={SPELLS.METEOR_TALENT.id} />}
        value={`${formatPercentage(this.meteorUtilization, 0)} %`}
        label="Meteor Utilization"
        tooltip={(
          <>
            This is a measure of how well you utilized your Meteor casts.

{this.totalMeteorCasts} Total Meteor casts

{this.totalMeteorCasts - this.badMeteor} Meteor casts during Rune of Power

{this.badMeteor} Meteor casts without Rune of Power

          </>
        )}
      />
    );
  }
}

export default Meteor;
<commit_msg>Update Meteor.js<commit_after>import React from 'react';
import SPELLS from 'common/SPELLS';
import SpellLink from 'common/SpellLink';
import SpellIcon from 'common/SpellIcon';
import { formatPercentage } from 'common/format';
import StatisticBox, { STATISTIC_ORDER } from 'interface/others/StatisticBox';
import Analyzer, { SELECTED_PLAYER } from 'parser/core/Analyzer';
import Events from 'parser/core/Events';
import AbilityTracker from 'parser/shared/modules/AbilityTracker';
import EnemyInstances from 'parser/shared/modules/EnemyInstances';

class Meteor extends Analyzer {
  static dependencies = {
    abilityTracker: AbilityTracker,
    enemies: EnemyInstances,
  };

  badMeteor = 0

  constructor(...args) {
    super(...args);
    this.hasMeteor = this.selectedCombatant.hasTalent(SPELLS.METEOR_TALENT.id);
    this.hasRuneOfPower = this.selectedCombatant.hasTalent(SPELLS.RUNE_OF_POWER_TALENT.id);
    this.active = this.hasMeteor && this.hasRuneOfPower ? true : false;

    if (!this.active) {
      return;
    }

    this.addEventListener(Events.cast.by(SELECTED_PLAYER).spell(SPELLS.METEOR_TALENT), this.onMeteor);
  }

  onMeteor(event) {
    if (!this.selectedCombatant.hasBuff(SPELLS.RUNE_OF_POWER_BUFF.id)) {
      this.badMeteor += 1;
    }
  }

  get totalMeteorCasts() {
    return this.abilityTracker.getAbility(SPELLS.METEOR_TALENT.id).casts;
  }

  get meteorUtilization() {
    return 1 - (this.badMeteor / this.totalMeteorCasts);
  }

  get meteorUtilSuggestionThresholds() {
    return {
      actual: this.meteorUtilization,
      isLessThan: {
        minor: 0.95,
        average: 0.90,
        major: 0.85,
      },
      style: 'percentage',
    };
  }

  suggestions(when) {
		when(this.meteorUtilSuggestionThresholds)
			.addSuggestion((suggest, actual, recommended) => {
				return suggest(<>You cast <SpellLink id={SPELLS.METEOR_TALENT.id} /> without <SpellLink id={SPELLS.RUNE_OF_POWER_TALENT.id} /> {this.badMeteor} times. In order to get the most out of <SpellLink id={SPELLS.METEOR_TALENT.id} /> you should always cast it while being buffed by <SpellLink id={SPELLS.RUNE_OF_POWER_TALENT.id} />.</>)
					.icon(SPELLS.METEOR_TALENT.icon)
					.actual(`${formatPercentage(this.meteorUtilization)}% Utilization`)
					.recommended(`<${formatPercentage(recommended)}% is recommended`);
			});
	}

	statistic() {
    return (
      <StatisticBox
        position={STATISTIC_ORDER.CORE(100)}
        icon={<SpellIcon id={SPELLS.METEOR_TALENT.id} />}
        value={`${formatPercentage(this.meteorUtilization, 0)} %`}
        label="Meteor Utilization"
        tooltip={(
          <>
            This is a measure of how well you utilized your Meteor casts.

{this.totalMeteorCasts} Total Meteor casts

{this.totalMeteorCasts - this.badMeteor} Meteor casts during Rune of Power

{this.badMeteor} Meteor casts without Rune of Power

          </>
        )}
      />
    );
  }
}

export default Meteor;
<|endoftext|>


<commit_before>describe("Awesomplete.$.fire", function () {

	$.fixture("plain");

	subject(function () {
		return Awesomplete.$.fire.bind(Awesomplete.$, this.element);
	});

	def("element", function () { return $("#plain") });

	beforeEach(function () {
		spyOn(this.element, "dispatchEvent");
	});

	it("fires event once", function () {
		this.subject("click");
		expect(this.element.dispatchEvent.calls.count()).toEqual(1);
	});

	describe("fires different event types", function () {
		it("fires click event", function () {
			this.subject("click");
			expect(this.element.dispatchEvent).toHaveBeenCalledWith(jasmine.objectContaining({ type: "click" }));
		});

		it("fires input event", function () {
			this.subject("input");
			expect(this.element.dispatchEvent).toHaveBeenCalledWith(jasmine.objectContaining({ type: "input" }));
		});
	});

	describe("sets event properties", function () {
		it("makes cancelable event", function () {
			this.subject("click");
			expect(this.element.dispatchEvent).toHaveBeenCalledWith(jasmine.objectContaining({ cancelable: true }));
		});

		it("can't make non cancelable event", function () {
			this.subject("click", { cancelable: false });
			expect(this.element.dispatchEvent).toHaveBeenCalledWith(jasmine.objectContaining({ cancelable: true }));
		});

		it("makes event that bubbles", function () {
			this.subject("click");
			expect(this.element.dispatchEvent).toHaveBeenCalledWith(jasmine.objectContaining({ bubbles: true }));
		});

		it("can't make event that does not bubble", function () {
			this.subject("click", { bubles: false });
			expect(this.element.dispatchEvent).toHaveBeenCalledWith(jasmine.objectContaining({ bubbles: true }));
		});

		it("sets properties on the event", function () {
			var properties = { text: "hello", preventDefault: $.noop };

			this.subject("click", properties);
			expect(this.element.dispatchEvent).toHaveBeenCalledWith(jasmine.objectContaining(properties));
		});
	});
});
<commit_msg>Fix typo in test<commit_after>describe("Awesomplete.$.fire", function () {

	$.fixture("plain");

	subject(function () {
		return Awesomplete.$.fire.bind(Awesomplete.$, this.element);
	});

	def("element", function () { return $("#plain") });

	beforeEach(function () {
		spyOn(this.element, "dispatchEvent");
	});

	it("fires event once", function () {
		this.subject("click");
		expect(this.element.dispatchEvent.calls.count()).toEqual(1);
	});

	describe("fires different event types", function () {
		it("fires click event", function () {
			this.subject("click");
			expect(this.element.dispatchEvent).toHaveBeenCalledWith(jasmine.objectContaining({ type: "click" }));
		});

		it("fires input event", function () {
			this.subject("input");
			expect(this.element.dispatchEvent).toHaveBeenCalledWith(jasmine.objectContaining({ type: "input" }));
		});
	});

	describe("sets event properties", function () {
		it("makes cancelable event", function () {
			this.subject("click");
			expect(this.element.dispatchEvent).toHaveBeenCalledWith(jasmine.objectContaining({ cancelable: true }));
		});

		it("can't make non cancelable event", function () {
			this.subject("click", { cancelable: false });
			expect(this.element.dispatchEvent).toHaveBeenCalledWith(jasmine.objectContaining({ cancelable: true }));
		});

		it("makes event that bubbles", function () {
			this.subject("click");
			expect(this.element.dispatchEvent).toHaveBeenCalledWith(jasmine.objectContaining({ bubbles: true }));
		});

		it("can't make event that does not bubble", function () {
			this.subject("click", { bubbles: false });
			expect(this.element.dispatchEvent).toHaveBeenCalledWith(jasmine.objectContaining({ bubbles: true }));
		});

		it("sets properties on the event", function () {
			var properties = { text: "hello", preventDefault: $.noop };

			this.subject("click", properties);
			expect(this.element.dispatchEvent).toHaveBeenCalledWith(jasmine.objectContaining(properties));
		});
	});
});
<|endoftext|>