<commit_before>
describe('HubSpot', function () {

var analytics = window.analytics || require('analytics')
  , assert = require('assert')
  , equal = require('equals')
  , sinon = require('sinon')
  , when = require('when');

var settings = {
  portalId: 62515
};

before(function (done) {
  this.timeout(10000);
  this.spy = sinon.spy();
  analytics.ready(this.spy);
  analytics.initialize({ HubSpot: settings });
  this.integration = analytics._integrations.HubSpot;
  this.options = this.integration.options;
  var stub = window._hsq.push;
  when(function () { return window._hsq.push != stub; }, done);
});

describe('#name', function () {
  it('HubSpot', function () {
    assert(this.integration.name == 'HubSpot');
  });
});

describe('#key', function () {
  it('portalId', function () {
    assert(this.integration.key == 'portalId');
  });
});

describe('#defaults', function () {
  it('portalId', function () {
    assert(this.integration.defaults.portalId === null);
  });
});

describe('#initialize', function () {
  it('should call ready', function () {
    assert(this.spy.called);
  });

  it('should store options', function () {
    assert(this.options.portalId == settings.portalId);
  });
});

describe('#identify', function () {
  beforeEach(function () {
    analytics.user().reset();
    this.stub = sinon.stub(window._hsq, 'push');
  });

  afterEach(function () {
    this.stub.restore();
  });

  it('shouldnt send traits without an email', function () {
    analytics.identify('id');
    assert(!this.stub.called);
  });

  it('should send traits with an email', function () {
    analytics.identify({ email: 'name@example.com' });
    assert(this.stub.calledWith(['identify', { email: 'name@example.com' }]));
  });

  it('should send an id and traits with an email', function () {
    analytics.identify('id', { email: 'name@example.com' });
    assert(this.stub.calledWith(['identify', {
      id: 'id',
      email: 'name@example.com'
    }]));
  });

  it('should send traits with an email id', function () {
    analytics.identify('name@example.com');
    assert(this.stub.calledWith(['identify', {
      id: 'name@example.com',
      email: 'name@example.com'
    }]));
  });

  it('should convert date traits to ms timestamps', function () {
    var date = new Date();
    analytics.identify({
      email: 'name@example.com',
      date: date
    });
    assert(this.stub.calledWith(['identify', {
      email: 'name@example.com',
      date: date.getTime()
    }]));
  });
});

describe('#track', function () {
  beforeEach(function () {
    analytics.user().reset();
    this.stub = sinon.stub(window._hsq, 'push');
  });

  afterEach(function () {
    this.stub.restore();
  });

  it('should send an event', function () {
    analytics.track('event');
    assert(this.stub.calledWith(['trackEvent', 'event', {}]));
  });

  it('should send an event and properties', function () {
    analytics.track('event', { property: true });
    assert(this.stub.calledWith(['trackEvent', 'event', { property: true }]));
  });

  it('should convert date properties to ms timestamps', function () {
    var date = new Date();
    analytics.track('event', { date: date });
    assert(this.stub.calledWith(['trackEvent', 'event', {
      date: date.getTime()
    }]));
  });
});

describe('#pageview', function () {
  beforeEach(function () {
    analytics.user().reset();
    this.stub = sinon.stub(window._hsq, 'push');
  });

  afterEach(function () {
    this.stub.restore();
  });

  it('should send a pageview', function () {
    analytics.pageview();
    assert(this.stub.calledWith(['_trackPageview']));
  });
});

});<commit_msg>hubspot: updating date tests to repro the exact case<commit_after>
describe('HubSpot', function () {

var analytics = window.analytics || require('analytics')
  , assert = require('assert')
  , equal = require('equals')
  , sinon = require('sinon')
  , when = require('when');

var settings = {
  portalId: 62515
};

before(function (done) {
  this.timeout(10000);
  this.spy = sinon.spy();
  analytics.ready(this.spy);
  analytics.initialize({ HubSpot: settings });
  this.integration = analytics._integrations.HubSpot;
  this.options = this.integration.options;
  var stub = window._hsq.push;
  when(function () { return window._hsq.push != stub; }, done);
});

describe('#name', function () {
  it('HubSpot', function () {
    assert(this.integration.name == 'HubSpot');
  });
});

describe('#key', function () {
  it('portalId', function () {
    assert(this.integration.key == 'portalId');
  });
});

describe('#defaults', function () {
  it('portalId', function () {
    assert(this.integration.defaults.portalId === null);
  });
});

describe('#initialize', function () {
  it('should call ready', function () {
    assert(this.spy.called);
  });

  it('should store options', function () {
    assert(this.options.portalId == settings.portalId);
  });
});

describe('#identify', function () {
  beforeEach(function () {
    analytics.user().reset();
    this.stub = sinon.stub(window._hsq, 'push');
  });

  afterEach(function () {
    this.stub.restore();
  });

  it('shouldnt send traits without an email', function () {
    analytics.identify('id');
    assert(!this.stub.called);
  });

  it('should send traits with an email', function () {
    analytics.identify({ email: 'name@example.com' });
    assert(this.stub.calledWith(['identify', { email: 'name@example.com' }]));
  });

  it('should send an id and traits with an email', function () {
    analytics.identify('id', { email: 'name@example.com' });
    assert(this.stub.calledWith(['identify', {
      id: 'id',
      email: 'name@example.com'
    }]));
  });

  it('should send traits with an email id', function () {
    analytics.identify('name@example.com');
    assert(this.stub.calledWith(['identify', {
      id: 'name@example.com',
      email: 'name@example.com'
    }]));
  });

  it('should convert date traits to ms timestamps', function () {
    var date = '2013-11-04';
    analytics.identify({
      email: 'name@example.com',
      date: date
    });
    assert(this.stub.calledWith(['identify', {
      email: 'name@example.com',
      date: 1383523200000
    }]));
  });
});

describe('#track', function () {
  beforeEach(function () {
    analytics.user().reset();
    this.stub = sinon.stub(window._hsq, 'push');
  });

  afterEach(function () {
    this.stub.restore();
  });

  it('should send an event', function () {
    analytics.track('event');
    assert(this.stub.calledWith(['trackEvent', 'event', {}]));
  });

  it('should send an event and properties', function () {
    analytics.track('event', { property: true });
    assert(this.stub.calledWith(['trackEvent', 'event', { property: true }]));
  });

  it('should convert date properties to ms timestamps', function () {
    var date = '2013-11-04';
    analytics.track('event', { date: date });
    assert(this.stub.calledWith(['trackEvent', 'event', {
      date: 1383523200000
    }]));
  });
});

describe('#pageview', function () {
  beforeEach(function () {
    analytics.user().reset();
    this.stub = sinon.stub(window._hsq, 'push');
  });

  afterEach(function () {
    this.stub.restore();
  });

  it('should send a pageview', function () {
    analytics.pageview();
    assert(this.stub.calledWith(['_trackPageview']));
  });
});

});<|endoftext|>


<commit_before>/* global CloudCmd, DOM */

'use strict';

const exec = require('execon');
const wrap = require('wraptile/legacy');
const supermenu = require('supermenu');
const createElement = require('@cloudcmd/create-element');

const {FS} = require('../../common/cloudfunc');
const {getIdBySrc} = require('../dom/load');
const RESTful = require('../dom/rest');

const {
    config,
    Key,
} = CloudCmd;

const {
    Buffer,
    Events,
    Dialog,
    Images,
} = DOM;

const Info = DOM.CurrentInfo;
const TITLE = 'Cloud Commander';
const alertNoFiles = wrap(Dialog.alert.noFiles)(TITLE);
const uploadTo = wrap(_uploadTo);

let MenuShowedName;
let MenuContext;
let MenuContextFile;

module.exports.ENABLED = false;

CloudCmd.Menu = exports;

module.exports.init = () => {
    const {isAuth, menuDataFile} = getFileMenuData();

    const NOT_FILE = true;
    const fm = DOM.getFM();
    const menuData = getMenuData(isAuth);
    const options = getOptions(NOT_FILE);
    const optionsFile = getOptions();

    MenuContext = supermenu(fm, options, menuData);
    MenuContextFile = supermenu(fm, optionsFile, menuDataFile);

    Events.addKey(listener);
};

module.exports.hide = hide;

function hide() {
    MenuContext.hide();
    MenuContextFile.hide();
}

module.exports.show = (position) => {
    const {x, y} = getPosition(position);

    MenuContext.show(x, y);
    MenuContextFile.show(x, y);

    Images.hide();
};

function getPosition(position) {
    if (position)
        return {
            x: position.x,
            y: position.y,
        };

    return getCurrentPosition();
}

function getMenuNameByEl(el) {
    if (!el)
        return 'context';

    const name = DOM.getCurrentName(el);

    if (name === '..')
        return 'context';

    return 'contextFile';
}

function getOptions(notFile) {
    let name;
    let func;

    if (notFile) {
        name = 'context';
        func = Key.unsetBind;
    } else {
        name = 'contextFile';
    }

    const options = {
        icon        : true,
        beforeClose : Key.setBind,
        beforeShow  : exec.with(beforeShow, func),
        beforeClick,
        name,
    };

    return options;
}

function getMenuData(isAuth) {
    const menu = {
        'Paste': Buffer.paste,
        'New': {
            'File': DOM.promptNewFile,
            'Directory': DOM.promptNewDir,
        },
        'Upload': () => {
            CloudCmd.Upload.show();
        },
        'Upload From Cloud': uploadFromCloud,
        '(Un)Select All': DOM.toggleAllSelectedFiles,
    };

    if (isAuth)
        menu['Log Out'] = CloudCmd.logOut;

    return menu;
}

function getFileMenuData() {
    const isAuth = CloudCmd.config('auth');
    const show = wrap((name) => {
        CloudCmd[name].show();
    });

    const menuBottom = getMenuData(isAuth);
    const menuTop = {
        'View': show('View'),
        'Edit': show('EditFile'),
        'Rename': () => {
            setTimeout(DOM.renameCurrent, 100);
        },
        'Delete': () => {
            CloudCmd.Operation.show('delete');
        },
        'Pack': () => {
            CloudCmd.Operation.show('pack');
        },
        'Extract': () => {
            CloudCmd.Operation.show('extract');
        },
        'Download': preDownload,
        'Upload To Cloud': uploadTo('Cloud'),
        'Cut': () => {
            isCurrent(Buffer.cut, alertNoFiles);
        },
        'Copy': () => {
            isCurrent(Buffer.copy, alertNoFiles);
        },
    };

    const menuDataFile = {
        ...menuTop,
        ...menuBottom,
    };

    return {
        isAuth,
        menuDataFile,
    };
}

function isCurrent(yesFn, noFn) {
    if (Info.name !== '..')
        return yesFn();

    noFn();
}

function isPath(x, y) {
    const {panel} = Info;
    const isEmptyRoot = !panel;

    if (isEmptyRoot)
        return false;

    const el = document.elementFromPoint(x, y);
    const elements = panel.querySelectorAll('[data-name="js-path"] *');
    const is = ~[].indexOf.call(elements, el);

    return is;
}

function beforeShow(callback, params) {
    const {name} = params;
    const el = DOM.getCurrentByPosition({
        x: params.x,
        y: params.y,
    });

    const menuName = getMenuNameByEl(el);
    let notShow = menuName === 'contextFile';

    if (params.name === 'contextFile') {
        notShow = !notShow;
    }

    if (!notShow)
        MenuShowedName = name;

    exec(callback);

    if (!notShow)
        notShow = isPath(params.x, params.y);

    return notShow;
}

function beforeClick(name) {
    return MenuShowedName !== name;
}

function _uploadTo(nameModule) {
    Info.getData((error, data) => {
        if (error)
            return;

        const {name} = Info;
        const execFrom = CloudCmd.execFromModule;

        execFrom(nameModule, 'uploadFile', name, data);
    });

    CloudCmd.log('Uploading to ' + name + '...');
}

function uploadFromCloud() {
    Images.show.load('top');

    CloudCmd.execFromModule('Cloud', 'saveFile', (currentName, data) => {
        const path = DOM.getCurrentDirPath() + currentName;

        RESTful.write(path, data, (error) => {
            if (error)
                return;

            CloudCmd.refresh({currentName});
        });
    });
}

function preDownload() {
    download(config('packer'));
}

function download(type) {
    const TIME = 30 * 1000;
    const {prefixURL} = CloudCmd;
    const PACK = '/pack';
    const date = Date.now();
    const files = DOM.getActiveFiles();

    if (!files.length)
        return alertNoFiles();

    files.forEach((file) => {
        const selected = DOM.isSelected(file);
        const isDir = DOM.isCurrentIsDir(file);
        const path = DOM.getCurrentPath(file);

        CloudCmd.log('downloading file ' + path + '...');
        /*
          * if we send ajax request -
          * no need in hash so we escape #
          * and all other characters, like "%"
          */
        const encodedPath = encodeURI(path).replace(/#/g, '%23');
        const id = getIdBySrc(path);

        let src;

        if (isDir)
            src = prefixURL + PACK + encodedPath + DOM.getPackerExt(type);
        else
            src = prefixURL + FS + encodedPath + '?download';

        const element = createElement('iframe', {
            id : id + '-' + date,
            async: false,
            className: 'hidden',
            src,
        });

        const {body} = document;
        const removeChild = body.removeChild.bind(body, element);

        setTimeout(removeChild, TIME);

        if (selected)
            DOM.toggleSelectedFile(file);
    });
}

function getCurrentPosition() {
    const current = Info.element;
    const rect = current.getBoundingClientRect();

    const position = {
        x: Math.round(rect.left + rect.width / 3),
        y: Math.round(rect.top),
    };

    return position;
}

function listener(event) {
    const {
        F9,
        ESC,
    } = Key;

    const key = event.keyCode;
    const isBind = Key.isBind();

    if (!isBind)
        return;

    if (key === ESC)
        return hide();

    if (key === F9) {
        const position = getCurrentPosition();
        MenuContext.show(position.x, position.y);

        event.preventDefault();
    }
}
<commit_msg>chore(menu) lint<commit_after>/* global CloudCmd, DOM */

'use strict';

const exec = require('execon');
const wrap = require('wraptile/legacy');
const supermenu = require('supermenu');
const createElement = require('@cloudcmd/create-element');

const {FS} = require('../../common/cloudfunc');
const {getIdBySrc} = require('../dom/load');
const RESTful = require('../dom/rest');

const {
    config,
    Key,
} = CloudCmd;

const {
    Buffer,
    Events,
    Dialog,
    Images,
} = DOM;

const Info = DOM.CurrentInfo;
const TITLE = 'Cloud Commander';
const alertNoFiles = wrap(Dialog.alert.noFiles)(TITLE);
const uploadTo = wrap(_uploadTo);

let MenuShowedName;
let MenuContext;
let MenuContextFile;

module.exports.ENABLED = false;

CloudCmd.Menu = exports;

module.exports.init = () => {
    const {
        isAuth,
        menuDataFile,
    } = getFileMenuData();

    const NOT_FILE = true;
    const fm = DOM.getFM();
    const menuData = getMenuData(isAuth);
    const options = getOptions(NOT_FILE);
    const optionsFile = getOptions();

    MenuContext = supermenu(fm, options, menuData);
    MenuContextFile = supermenu(fm, optionsFile, menuDataFile);

    Events.addKey(listener);
};

module.exports.hide = hide;

function hide() {
    MenuContext.hide();
    MenuContextFile.hide();
}

module.exports.show = (position) => {
    const {x, y} = getPosition(position);

    MenuContext.show(x, y);
    MenuContextFile.show(x, y);

    Images.hide();
};

function getPosition(position) {
    if (position)
        return {
            x: position.x,
            y: position.y,
        };

    return getCurrentPosition();
}

function getMenuNameByEl(el) {
    if (!el)
        return 'context';

    const name = DOM.getCurrentName(el);

    if (name === '..')
        return 'context';

    return 'contextFile';
}

function getOptions(notFile) {
    let name;
    let func;

    if (notFile) {
        name = 'context';
        func = Key.unsetBind;
    } else {
        name = 'contextFile';
    }

    const options = {
        icon        : true,
        beforeClose : Key.setBind,
        beforeShow  : exec.with(beforeShow, func),
        beforeClick,
        name,
    };

    return options;
}

function getMenuData(isAuth) {
    const menu = {
        'Paste': Buffer.paste,
        'New': {
            'File': DOM.promptNewFile,
            'Directory': DOM.promptNewDir,
        },
        'Upload': () => {
            CloudCmd.Upload.show();
        },
        'Upload From Cloud': uploadFromCloud,
        '(Un)Select All': DOM.toggleAllSelectedFiles,
    };

    if (isAuth)
        menu['Log Out'] = CloudCmd.logOut;

    return menu;
}

function getFileMenuData() {
    const isAuth = CloudCmd.config('auth');
    const show = wrap((name) => {
        CloudCmd[name].show();
    });

    const menuBottom = getMenuData(isAuth);
    const menuTop = {
        'View': show('View'),
        'Edit': show('EditFile'),
        'Rename': () => {
            setTimeout(DOM.renameCurrent, 100);
        },
        'Delete': () => {
            CloudCmd.Operation.show('delete');
        },
        'Pack': () => {
            CloudCmd.Operation.show('pack');
        },
        'Extract': () => {
            CloudCmd.Operation.show('extract');
        },
        'Download': preDownload,
        'Upload To Cloud': uploadTo('Cloud'),
        'Cut': () => {
            isCurrent(Buffer.cut, alertNoFiles);
        },
        'Copy': () => {
            isCurrent(Buffer.copy, alertNoFiles);
        },
    };

    const menuDataFile = {
        ...menuTop,
        ...menuBottom,
    };

    return {
        isAuth,
        menuDataFile,
    };
}

function isCurrent(yesFn, noFn) {
    if (Info.name !== '..')
        return yesFn();

    noFn();
}

function isPath(x, y) {
    const {panel} = Info;
    const isEmptyRoot = !panel;

    if (isEmptyRoot)
        return false;

    const el = document.elementFromPoint(x, y);
    const elements = panel.querySelectorAll('[data-name="js-path"] *');
    const is = ~[].indexOf.call(elements, el);

    return is;
}

function beforeShow(callback, params) {
    const {name} = params;
    const el = DOM.getCurrentByPosition({
        x: params.x,
        y: params.y,
    });

    const menuName = getMenuNameByEl(el);
    let notShow = menuName === 'contextFile';

    if (params.name === 'contextFile') {
        notShow = !notShow;
    }

    if (!notShow)
        MenuShowedName = name;

    exec(callback);

    if (!notShow)
        notShow = isPath(params.x, params.y);

    return notShow;
}

function beforeClick(name) {
    return MenuShowedName !== name;
}

function _uploadTo(nameModule) {
    Info.getData((error, data) => {
        if (error)
            return;

        const {name} = Info;
        const execFrom = CloudCmd.execFromModule;

        execFrom(nameModule, 'uploadFile', name, data);
    });

    CloudCmd.log('Uploading to ' + name + '...');
}

function uploadFromCloud() {
    Images.show.load('top');

    CloudCmd.execFromModule('Cloud', 'saveFile', (currentName, data) => {
        const path = DOM.getCurrentDirPath() + currentName;

        RESTful.write(path, data, (error) => {
            if (error)
                return;

            CloudCmd.refresh({currentName});
        });
    });
}

function preDownload() {
    download(config('packer'));
}

function download(type) {
    const TIME = 30 * 1000;
    const {prefixURL} = CloudCmd;
    const PACK = '/pack';
    const date = Date.now();
    const files = DOM.getActiveFiles();

    if (!files.length)
        return alertNoFiles();

    files.forEach((file) => {
        const selected = DOM.isSelected(file);
        const isDir = DOM.isCurrentIsDir(file);
        const path = DOM.getCurrentPath(file);

        CloudCmd.log('downloading file ' + path + '...');
        /*
          * if we send ajax request -
          * no need in hash so we escape #
          * and all other characters, like "%"
          */
        const encodedPath = encodeURI(path).replace(/#/g, '%23');
        const id = getIdBySrc(path);

        let src;

        if (isDir)
            src = prefixURL + PACK + encodedPath + DOM.getPackerExt(type);
        else
            src = prefixURL + FS + encodedPath + '?download';

        const element = createElement('iframe', {
            id : id + '-' + date,
            async: false,
            className: 'hidden',
            src,
        });

        const {body} = document;
        const removeChild = body.removeChild.bind(body, element);

        setTimeout(removeChild, TIME);

        if (selected)
            DOM.toggleSelectedFile(file);
    });
}

function getCurrentPosition() {
    const current = Info.element;
    const rect = current.getBoundingClientRect();

    const position = {
        x: Math.round(rect.left + rect.width / 3),
        y: Math.round(rect.top),
    };

    return position;
}

function listener(event) {
    const {
        F9,
        ESC,
    } = Key;

    const key = event.keyCode;
    const isBind = Key.isBind();

    if (!isBind)
        return;

    if (key === ESC)
        return hide();

    if (key === F9) {
        const position = getCurrentPosition();
        MenuContext.show(position.x, position.y);

        event.preventDefault();
    }
}
<|endoftext|>


<commit_before>'use strict'

const t = require('tap')
const test = t.test
const sget = require('simple-get').concat
const http = require('http')

const Reply = require('../../lib/reply')

test('Once called, Reply should return an object with methods', t => {
  t.plan(8)
  const request = { req: 'req' }
  const response = { res: 'res' }
  function handle () {}
  const reply = new Reply(request, response, handle)
  t.is(typeof reply, 'object')
  t.is(typeof reply.send, 'function')
  t.is(typeof reply.code, 'function')
  t.is(typeof reply.header, 'function')
  t.is(typeof reply.serialize, 'function')
  t.strictEqual(reply._req, request)
  t.strictEqual(reply.res, response)
  t.strictEqual(reply.context, handle)
})

test('reply.send throw with circular JSON', t => {
  t.plan(1)
  const request = {}
  const response = { setHeader: () => {} }
  const reply = new Reply(request, response, null)
  t.throws(() => {
    var obj = {}
    obj.obj = obj
    reply.send(JSON.stringify(obj))
  })
})

test('reply.serializer should set a custom serializer', t => {
  t.plan(2)
  const reply = new Reply(null, null, null)
  t.equal(reply._serializer, null)
  reply.serializer('serializer')
  t.equal(reply._serializer, 'serializer')
})

test('within an instance', t => {
  const fastify = require('../..')()
  const test = t.test

  fastify.get('/', function (req, reply) {
    reply.code(200)
    reply.header('Content-Type', 'text/plain')
    reply.send('hello world!')
  })

  fastify.get('/auto-type', function (req, reply) {
    reply.code(200)
    reply.type('text/plain')
    reply.send('hello world!')
  })

  fastify.get('/auto-status-code', function (req, reply) {
    reply.send('hello world!')
  })

  fastify.get('/redirect', function (req, reply) {
    reply.redirect('/')
  })

  fastify.get('/redirect-code', function (req, reply) {
    reply.redirect(301, '/')
  })

  fastify.get('/custom-serializer', function (req, reply) {
    reply.code(200)
    reply.type('text/plain')
    reply.serializer(function (body) {
      return require('querystring').stringify(body)
    })
    reply.send({hello: 'world!'})
  })

  fastify.listen(0, err => {
    t.error(err)
    fastify.server.unref()

    test('custom serializer should be used', t => {
      t.plan(3)
      sget({
        method: 'GET',
        url: 'http://localhost:' + fastify.server.address().port + '/custom-serializer'
      }, (err, response, body) => {
        t.error(err)
        t.strictEqual(response.headers['content-type'], 'text/plain')
        t.deepEqual(body.toString(), 'hello=world!')
      })
    })

    test('status code and content-type should be correct', t => {
      t.plan(4)
      sget({
        method: 'GET',
        url: 'http://localhost:' + fastify.server.address().port
      }, (err, response, body) => {
        t.error(err)
        t.strictEqual(response.statusCode, 200)
        t.strictEqual(response.headers['content-type'], 'text/plain')
        t.deepEqual(body.toString(), 'hello world!')
      })
    })

    test('auto status code shoud be 200', t => {
      t.plan(3)
      sget({
        method: 'GET',
        url: 'http://localhost:' + fastify.server.address().port + '/auto-status-code'
      }, (err, response, body) => {
        t.error(err)
        t.strictEqual(response.statusCode, 200)
        t.deepEqual(JSON.parse(body), 'hello world!')
      })
    })

    test('auto type shoud be text/plain', t => {
      t.plan(3)
      sget({
        method: 'GET',
        url: 'http://localhost:' + fastify.server.address().port + '/auto-type'
      }, (err, response, body) => {
        t.error(err)
        t.strictEqual(response.headers['content-type'], 'text/plain')
        t.deepEqual(body.toString(), 'hello world!')
      })
    })

    test('redirect to `/` - 1', t => {
      t.plan(1)

      http.get('http://localhost:' + fastify.server.address().port + '/redirect', function (response) {
        t.strictEqual(response.statusCode, 302)
      })
    })

    test('redirect to `/` - 2', t => {
      t.plan(1)

      http.get('http://localhost:' + fastify.server.address().port + '/redirect-code', function (response) {
        t.strictEqual(response.statusCode, 301)
      })
    })

    test('redirect to `/` - 3', t => {
      t.plan(4)
      sget({
        method: 'GET',
        url: 'http://localhost:' + fastify.server.address().port + '/redirect'
      }, (err, response, body) => {
        t.error(err)
        t.strictEqual(response.statusCode, 200)
        t.strictEqual(response.headers['content-type'], 'text/plain')
        t.deepEqual(body.toString(), 'hello world!')
      })
    })

    test('redirect to `/` - 4', t => {
      t.plan(4)
      sget({
        method: 'GET',
        url: 'http://localhost:' + fastify.server.address().port + '/redirect-code'
      }, (err, response, body) => {
        t.error(err)
        t.strictEqual(response.statusCode, 200)
        t.strictEqual(response.headers['content-type'], 'text/plain')
        t.deepEqual(body.toString(), 'hello world!')
      })
    })

    t.end()
  })
})
<commit_msg>add test case<commit_after>'use strict'

const t = require('tap')
const test = t.test
const sget = require('simple-get').concat
const http = require('http')
const zlib = require('zlib')

const Reply = require('../../lib/reply')

test('Once called, Reply should return an object with methods', t => {
  t.plan(8)
  const request = { req: 'req' }
  const response = { res: 'res' }
  function handle () {}
  const reply = new Reply(request, response, handle)
  t.is(typeof reply, 'object')
  t.is(typeof reply.send, 'function')
  t.is(typeof reply.code, 'function')
  t.is(typeof reply.header, 'function')
  t.is(typeof reply.serialize, 'function')
  t.strictEqual(reply._req, request)
  t.strictEqual(reply.res, response)
  t.strictEqual(reply.context, handle)
})

test('reply.send throw with circular JSON', t => {
  t.plan(1)
  const request = {}
  const response = { setHeader: () => {} }
  const reply = new Reply(request, response, null)
  t.throws(() => {
    var obj = {}
    obj.obj = obj
    reply.send(JSON.stringify(obj))
  })
})

test('reply.serializer should set a custom serializer', t => {
  t.plan(2)
  const reply = new Reply(null, null, null)
  t.equal(reply._serializer, null)
  reply.serializer('serializer')
  t.equal(reply._serializer, 'serializer')
})

test('within an instance', t => {
  const fastify = require('../..')()
  const test = t.test

  fastify.get('/', function (req, reply) {
    reply.code(200)
    reply.header('Content-Type', 'text/plain')
    reply.send('hello world!')
  })

  fastify.get('/auto-type', function (req, reply) {
    reply.code(200)
    reply.type('text/plain')
    reply.send('hello world!')
  })

  fastify.get('/auto-status-code', function (req, reply) {
    reply.send('hello world!')
  })

  fastify.get('/redirect', function (req, reply) {
    reply.redirect('/')
  })

  fastify.get('/redirect-code', function (req, reply) {
    reply.redirect(301, '/')
  })

  fastify.get('/custom-serializer', function (req, reply) {
    reply.code(200)
    reply.type('text/plain')
    reply.serializer(function (body) {
      return require('querystring').stringify(body)
    })
    reply.send({hello: 'world!'})
  })

  fastify.listen(0, err => {
    t.error(err)
    fastify.server.unref()

    test('custom serializer should be used', t => {
      t.plan(3)
      sget({
        method: 'GET',
        url: 'http://localhost:' + fastify.server.address().port + '/custom-serializer'
      }, (err, response, body) => {
        t.error(err)
        t.strictEqual(response.headers['content-type'], 'text/plain')
        t.deepEqual(body.toString(), 'hello=world!')
      })
    })

    test('status code and content-type should be correct', t => {
      t.plan(4)
      sget({
        method: 'GET',
        url: 'http://localhost:' + fastify.server.address().port
      }, (err, response, body) => {
        t.error(err)
        t.strictEqual(response.statusCode, 200)
        t.strictEqual(response.headers['content-type'], 'text/plain')
        t.deepEqual(body.toString(), 'hello world!')
      })
    })

    test('auto status code shoud be 200', t => {
      t.plan(3)
      sget({
        method: 'GET',
        url: 'http://localhost:' + fastify.server.address().port + '/auto-status-code'
      }, (err, response, body) => {
        t.error(err)
        t.strictEqual(response.statusCode, 200)
        t.deepEqual(JSON.parse(body), 'hello world!')
      })
    })

    test('auto type shoud be text/plain', t => {
      t.plan(3)
      sget({
        method: 'GET',
        url: 'http://localhost:' + fastify.server.address().port + '/auto-type'
      }, (err, response, body) => {
        t.error(err)
        t.strictEqual(response.headers['content-type'], 'text/plain')
        t.deepEqual(body.toString(), 'hello world!')
      })
    })

    test('redirect to `/` - 1', t => {
      t.plan(1)

      http.get('http://localhost:' + fastify.server.address().port + '/redirect', function (response) {
        t.strictEqual(response.statusCode, 302)
      })
    })

    test('redirect to `/` - 2', t => {
      t.plan(1)

      http.get('http://localhost:' + fastify.server.address().port + '/redirect-code', function (response) {
        t.strictEqual(response.statusCode, 301)
      })
    })

    test('redirect to `/` - 3', t => {
      t.plan(4)
      sget({
        method: 'GET',
        url: 'http://localhost:' + fastify.server.address().port + '/redirect'
      }, (err, response, body) => {
        t.error(err)
        t.strictEqual(response.statusCode, 200)
        t.strictEqual(response.headers['content-type'], 'text/plain')
        t.deepEqual(body.toString(), 'hello world!')
      })
    })

    test('redirect to `/` - 4', t => {
      t.plan(4)
      sget({
        method: 'GET',
        url: 'http://localhost:' + fastify.server.address().port + '/redirect-code'
      }, (err, response, body) => {
        t.error(err)
        t.strictEqual(response.statusCode, 200)
        t.strictEqual(response.headers['content-type'], 'text/plain')
        t.deepEqual(body.toString(), 'hello world!')
      })
    })

    t.end()
  })
})

test('use reply.serialize in onSend hook', t => {
  t.plan(4)

  const fastify = require('../..')()
  fastify.addHook('onSend', (request, reply, payload, next) => {
    function _serialize () {
      const _payload = reply.serialize(reply.context, payload, reply.res.statusCode)
      return zlib.gzipSync(_payload)
    }
    reply.serializer(_serialize)
    reply.header('Content-Encoding', 'gzip')
    next()
  })
  fastify.get('/', (request, reply) => {
    reply.send('hello world!')
  })
  fastify.listen(0, err => {
    t.error(err)
    fastify.server.unref()
    sget({
      method: 'GET',
      url: 'http://localhost:' + fastify.server.address().port
    }, (err, response, body) => {
      t.error(err)
      t.strictEqual(response.statusCode, 200)
      t.strictEqual(JSON.parse(body), 'hello world!')
    })
  })
})
<|endoftext|>


<commit_before>'use strict';

var _ = require('lodash');

/*----------------------------------------------------------------------------*/

/**
 * Creates a hash object. If a `properties` object is provided, its own
 * enumerable properties are assigned to the created object.
 *
 * @memberOf util
 * @param {Object} [properties] The properties to assign to the object.
 * @returns {Object} Returns the new hash object.
 */
function Hash(properties) {
  return _.transform(properties, function(result, value, key) {
    result[key] = (_.isPlainObject(value) && !(value instanceof Hash))
      ? new Hash(value)
      : value;
  }, this);
}

Hash.prototype = Object.create(null);

module.exports = {
  'Hash': Hash
};
<commit_msg>Update tmp/bower_components/lodash/lib/common/util.js<commit_after><|endoftext|>


<commit_before>import test from 'ava';
import examplesLoader from '../loaders/examples.loader';

test('should return valid, parsable JS', t => {
	let exampleMarkdown = `
# header

text

\`\`\`

\`\`\`
`;
	let result = examplesLoader.call({}, exampleMarkdown);
	t.truthy(result);
	t.notThrows(() => new Function(result), SyntaxError);  // eslint-disable-line no-new-func
});

// componentName query option

test('should replace all occurrences of __COMPONENT__ with provided query.componentName', t => {
	const exampleMarkdown = `

	<__COMPONENT__>
		text
		Name of component: __COMPONENT__
	</__COMPONENT__>
	<__COMPONENT__ />

`;

	const result = examplesLoader.call({ query: '?componentName=FooComponent' }, exampleMarkdown);
	t.notRegex(result, /__COMPONENT__/);
	t.regex(result, /FooComponent/);
	t.is(result.match(/FooComponent/g).length, 4);
});
<commit_msg>Add require to test example.<commit_after>import test from 'ava';
import examplesLoader from '../loaders/examples.loader';

test('should return valid, parsable JS', t => {
	let exampleMarkdown = `
# header

	const _ = require('lodash');

text

\`\`\`

\`\`\`
`;
	let result = examplesLoader.call({}, exampleMarkdown);
	t.truthy(result);
	t.notThrows(() => new Function(result), SyntaxError);  // eslint-disable-line no-new-func
});

// componentName query option

test('should replace all occurrences of __COMPONENT__ with provided query.componentName', t => {
	const exampleMarkdown = `

	<__COMPONENT__>
		text
		Name of component: __COMPONENT__
	</__COMPONENT__>
	<__COMPONENT__ />

`;

	const result = examplesLoader.call({ query: '?componentName=FooComponent' }, exampleMarkdown);
	t.notRegex(result, /__COMPONENT__/);
	t.regex(result, /FooComponent/);
	t.is(result.match(/FooComponent/g).length, 4);
});
<|endoftext|>


<commit_before>const path = require('path');
const fs = require('fs');
const webpack = require('webpack');
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;

// const BabiliWebpackPlugin = require('babili-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { VueLoaderPlugin } = require('vue-loader');
const { dependencies } = require('../package.json');

/**
 * List of node_modules to include in webpack bundle
 *
 * Required for specific packages like Vue UI libraries
 * that provide pure *.vue files that need compiling
 * https://simulatedgreg.gitbooks.io/electron-vue/content/en/webpack-configurations.html#white-listing-externals
 */
const whiteListedModules = [
    'vue',
    'vuetify',
    'vue-router',
    'vue-i18n',
    'vuex',
    'maven-artifact-version',
    '@vue/composition-api',
    '@xmcl/text-component',
    '@xmcl/model',
    'three',
    'three-orbit-controls',
    'vue-virtual-scroll-list'
];

/**
 * @type {import('webpack').Configuration}
 */
const rendererConfig = {
    mode: process.env.NODE_ENV,
    // devtool: 'source-map',
    devtool: '#eval-source-map',
    // devtool: '#cheap-module-eval-source-map',
    entry: {
        renderer: path.join(__dirname, '../src/renderer/windows/main/index.ts'),
        logger: path.join(__dirname, '../src/renderer/windows/logger/index.ts'),
        setup: path.join(__dirname, '../src/renderer/windows/setup/index.ts'),
    },
    externals: [
        ...Object.keys(dependencies || {}).filter(d => !whiteListedModules.includes(d)),
    ],
    module: {
        rules: [
            {
                test: /\.styl$/,
                loader: 'css-loader!stylus-loader?paths=node_modules/bootstrap-stylus/stylus/'
            },
            {
                test: /\.css$/,
                use: ['vue-style-loader', 'css-loader'],
            },
            {
                test: /\.vue$/,
                use: {
                    loader: 'vue-loader',
                    options: {
                        extractCSS: process.env.NODE_ENV === 'production',
                        loaders: {
                            sass: 'vue-style-loader!css-loader!sass-loader?indentedSyntax=1',
                            scss: 'vue-style-loader!css-loader!sass-loader',
                        },
                    },
                },
            },
            {
                test: /\.ts$/,
                use: [
                    'cache-loader',
                    {
                        loader: 'thread-loader',
                    },
                    {
                        loader: 'ts-loader',
                        options: {
                            appendTsSuffixTo: [/\.vue$/],
                            happyPackMode: true
                        },
                    }
                ],
                exclude: /node_modules/,
                include: [path.join(__dirname, '../src/renderer'), path.join(__dirname, '../src/universal')],
            },
            {
                test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
                use: {
                    loader: 'url-loader',
                    query: {
                        limit: 10000,
                        name: 'imgs/[name].[ext]',
                    },
                },
            },
            {
                test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
                use: {
                    loader: 'url-loader',
                    query: {
                        limit: 10000,
                        name: 'fonts/[name].[ext]',
                    },
                },
            },
        ],
    },
    node: {
        __dirname: process.env.NODE_ENV !== 'production',
        __filename: process.env.NODE_ENV !== 'production',
    },
    plugins: [
        // new ForkTsCheckerWebpackPlugin({
        //     vue: true,
        //     // eslint: true,
        //     tsconfig: path.resolve(__dirname, '../tsconfig.json'),
        // }),
        new VueLoaderPlugin(),
        new MiniCssExtractPlugin({ filename: 'styles.css' }),
        new HtmlWebpackPlugin({
            filename: 'main.html',
            chunks: ['renderer'],
            template: path.resolve(__dirname, '../src/index.ejs'),
            minify: {
                collapseWhitespace: true,
                removeAttributeQuotes: true,
                removeComments: true,
            },
            nodeModules: process.env.NODE_ENV !== 'production'
                ? path.resolve(__dirname, '../node_modules')
                : false,
        }),
        new HtmlWebpackPlugin({
            filename: 'logger.html',
            chunks: ['logger'],
            template: path.resolve(__dirname, '../src/index.ejs'),
            minify: {
                collapseWhitespace: true,
                removeAttributeQuotes: true,
                removeComments: true,
            },
            nodeModules: process.env.NODE_ENV !== 'production'
                ? path.resolve(__dirname, '../node_modules')
                : false,
        }),
        new HtmlWebpackPlugin({
            filename: 'setup.html',
            chunks: ['setup'],
            template: path.resolve(__dirname, '../src/index.ejs'),
            minify: {
                collapseWhitespace: true,
                removeAttributeQuotes: true,
                removeComments: true,
            },
            nodeModules: process.env.NODE_ENV !== 'production'
                ? path.resolve(__dirname, '../node_modules')
                : false,
        }),
        new webpack.NoEmitOnErrorsPlugin(),
    ],
    output: {
        filename: '[name].js',
        libraryTarget: 'var',
        path: path.join(__dirname, '../dist/electron'),
    },
    resolve: {
        alias: {
            '@renderer': path.join(__dirname, '../src/renderer'),
            '@': path.join(__dirname, '../src/renderer'),
            '@universal': path.join(__dirname, '../src/universal'),
        },
        extensions: ['.ts', '.js', '.vue', '.json', '.css', '.node'],
    },
    target: 'web',
};

/**
 * Adjust rendererConfig for development settings
 */
if (process.env.NODE_ENV !== 'production') {
    rendererConfig.plugins.push(
        new webpack.DefinePlugin({
            __static: `"${path.join(__dirname, '../static').replace(/\\/g, '\\\\')}"`,
        }),
    );
}

/**
 * Adjust rendererConfig for production settings
 */
if (process.env.NODE_ENV === 'production') {
    rendererConfig.devtool = '';

    rendererConfig.plugins.push(
        new CopyWebpackPlugin([
            {
                from: path.join(__dirname, '../static'),
                to: path.join(__dirname, '../dist/electron/static'),
                ignore: ['.*'],
            },
        ]),
        new webpack.DefinePlugin({
            'process.env.NODE_ENV': '"production"',
        }),
        new webpack.LoaderOptionsPlugin({
            minimize: true,
        }),
        new BundleAnalyzerPlugin({
            analyzerMode: 'static',
            generateStatsFile: true,
            reportFilename: path.join(__dirname, '../dist/renderer.report.html'),
            statsFilename: path.join(__dirname, '../dist/renderer.stat.json'),
            openAnalyzer: false,
        }),
    );
}

module.exports = rendererConfig;
<commit_msg>chore: add webp bundle to webpack<commit_after>const path = require('path');
const fs = require('fs');
const webpack = require('webpack');
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;

// const BabiliWebpackPlugin = require('babili-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { VueLoaderPlugin } = require('vue-loader');
const { dependencies } = require('../package.json');

/**
 * List of node_modules to include in webpack bundle
 *
 * Required for specific packages like Vue UI libraries
 * that provide pure *.vue files that need compiling
 * https://simulatedgreg.gitbooks.io/electron-vue/content/en/webpack-configurations.html#white-listing-externals
 */
const whiteListedModules = [
    'vue',
    'vuetify',
    'vue-router',
    'vue-i18n',
    'vuex',
    'maven-artifact-version',
    '@vue/composition-api',
    '@xmcl/text-component',
    '@xmcl/model',
    'three',
    'three-orbit-controls',
    'vue-virtual-scroll-list'
];

/**
 * @type {import('webpack').Configuration}
 */
const rendererConfig = {
    mode: process.env.NODE_ENV,
    // devtool: 'source-map',
    devtool: '#eval-source-map',
    // devtool: '#cheap-module-eval-source-map',
    entry: {
        renderer: path.join(__dirname, '../src/renderer/windows/main/index.ts'),
        logger: path.join(__dirname, '../src/renderer/windows/logger/index.ts'),
        setup: path.join(__dirname, '../src/renderer/windows/setup/index.ts'),
    },
    externals: [
        ...Object.keys(dependencies || {}).filter(d => !whiteListedModules.includes(d)),
    ],
    module: {
        rules: [
            {
                test: /\.styl$/,
                loader: 'css-loader!stylus-loader?paths=node_modules/bootstrap-stylus/stylus/'
            },
            {
                test: /\.css$/,
                use: ['vue-style-loader', 'css-loader'],
            },
            {
                test: /\.vue$/,
                use: {
                    loader: 'vue-loader',
                    options: {
                        extractCSS: process.env.NODE_ENV === 'production',
                        loaders: {
                            sass: 'vue-style-loader!css-loader!sass-loader?indentedSyntax=1',
                            scss: 'vue-style-loader!css-loader!sass-loader',
                        },
                    },
                },
            },
            {
                test: /\.ts$/,
                use: [
                    'cache-loader',
                    {
                        loader: 'thread-loader',
                    },
                    {
                        loader: 'ts-loader',
                        options: {
                            appendTsSuffixTo: [/\.vue$/],
                            happyPackMode: true
                        },
                    }
                ],
                exclude: /node_modules/,
                include: [path.join(__dirname, '../src/renderer'), path.join(__dirname, '../src/universal')],
            },
            {
                test: /\.(png|jpe?g|gif|svg|webp)(\?.*)?$/,
                use: {
                    loader: 'url-loader',
                    query: {
                        limit: 10000,
                        name: 'imgs/[name].[ext]',
                    },
                },
            },
            {
                test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
                use: {
                    loader: 'url-loader',
                    query: {
                        limit: 10000,
                        name: 'fonts/[name].[ext]',
                    },
                },
            },
        ],
    },
    node: {
        __dirname: process.env.NODE_ENV !== 'production',
        __filename: process.env.NODE_ENV !== 'production',
    },
    plugins: [
        // new ForkTsCheckerWebpackPlugin({
        //     vue: true,
        //     // eslint: true,
        //     tsconfig: path.resolve(__dirname, '../tsconfig.json'),
        // }),
        new VueLoaderPlugin(),
        new MiniCssExtractPlugin({ filename: 'styles.css' }),
        new HtmlWebpackPlugin({
            filename: 'main.html',
            chunks: ['renderer'],
            template: path.resolve(__dirname, '../src/index.ejs'),
            minify: {
                collapseWhitespace: true,
                removeAttributeQuotes: true,
                removeComments: true,
            },
            nodeModules: process.env.NODE_ENV !== 'production'
                ? path.resolve(__dirname, '../node_modules')
                : false,
        }),
        new HtmlWebpackPlugin({
            filename: 'logger.html',
            chunks: ['logger'],
            template: path.resolve(__dirname, '../src/index.ejs'),
            minify: {
                collapseWhitespace: true,
                removeAttributeQuotes: true,
                removeComments: true,
            },
            nodeModules: process.env.NODE_ENV !== 'production'
                ? path.resolve(__dirname, '../node_modules')
                : false,
        }),
        new HtmlWebpackPlugin({
            filename: 'setup.html',
            chunks: ['setup'],
            template: path.resolve(__dirname, '../src/index.ejs'),
            minify: {
                collapseWhitespace: true,
                removeAttributeQuotes: true,
                removeComments: true,
            },
            nodeModules: process.env.NODE_ENV !== 'production'
                ? path.resolve(__dirname, '../node_modules')
                : false,
        }),
        new webpack.NoEmitOnErrorsPlugin(),
    ],
    output: {
        filename: '[name].js',
        libraryTarget: 'var',
        path: path.join(__dirname, '../dist/electron'),
    },
    resolve: {
        alias: {
            '@renderer': path.join(__dirname, '../src/renderer'),
            '@': path.join(__dirname, '../src/renderer'),
            '@universal': path.join(__dirname, '../src/universal'),
        },
        extensions: ['.ts', '.js', '.vue', '.json', '.css', '.node'],
    },
    target: 'web',
};

/**
 * Adjust rendererConfig for development settings
 */
if (process.env.NODE_ENV !== 'production') {
    rendererConfig.plugins.push(
        new webpack.DefinePlugin({
            __static: `"${path.join(__dirname, '../static').replace(/\\/g, '\\\\')}"`,
        }),
    );
}

/**
 * Adjust rendererConfig for production settings
 */
if (process.env.NODE_ENV === 'production') {
    rendererConfig.devtool = '';

    rendererConfig.plugins.push(
        new CopyWebpackPlugin([
            {
                from: path.join(__dirname, '../static'),
                to: path.join(__dirname, '../dist/electron/static'),
                ignore: ['.*'],
            },
        ]),
        new webpack.DefinePlugin({
            'process.env.NODE_ENV': '"production"',
        }),
        new webpack.LoaderOptionsPlugin({
            minimize: true,
        }),
        new BundleAnalyzerPlugin({
            analyzerMode: 'static',
            generateStatsFile: true,
            reportFilename: path.join(__dirname, '../dist/renderer.report.html'),
            statsFilename: path.join(__dirname, '../dist/renderer.stat.json'),
            openAnalyzer: false,
        }),
    );
}

module.exports = rendererConfig;
<|endoftext|>


<commit_before>'use strict';

describe('WebSocket', function () {
  var socket,
      TEST_SERVER_URL = 'ws://localhost:8001',
      flag;

  beforeEach(function (done) {
    socket = new WebSocket(TEST_SERVER_URL);
    socket.addEventListener('open', done);
  });

  afterEach(function () {
    socket.close();
  });

  it('should work with addEventListener', function () {
    var parent = window.zone;

    socket.addEventListener('message', function (contents) {
      expect(window.zone.parent).toBe(parent);
      expect(contents).toBe('HI');
      done();
    });
    socket.send('hi');
  });

  it('should respect removeEventListener', function (done) {
    var log = '';
    function logOnMessage () {
      log += 'a';

      expect(log).toEqual('a');

      socket.removeEventListener('message', logOnMessage);
      socket.send('hi');

      setTimeout(function () {
        expect(log).toEqual('a');
        done();
      }, 10);
    };

    socket.addEventListener('message', logOnMessage);
    socket.send('hi');
  });

  it('should work with onmessage', function (done) {
    var parent = window.zone;
    socket.onmessage = function (contents) {
      expect(window.zone.parent).toBe(parent);
      expect(contents.data).toBe('hi');
      done();
    };
    socket.send('hi');
  });

  it('should only allow one onmessage handler', function (done) {
    var log = '';
    socket.onmessage = function () {
      log += 'a';
      expect(log).toEqual('b');
      done();
    };
    socket.onmessage = function () {
      log += 'b';
      expect(log).toEqual('b');
      done();
    };

    socket.send('hi');
  });

  it('should handle removing onmessage', function () {
    var log = '';
    socket.onmessage = function () {
      log += 'a';
    };
    socket.onmessage = null;

    socket.send('hi');
    expect(log).toEqual('');
  });

});
<commit_msg>test(WebSocket): fix some tests<commit_after>'use strict';

describe('WebSocket', function () {
  var socket,
      TEST_SERVER_URL = 'ws://localhost:8001',
      flag;

  beforeEach(function (done) {
    socket = new WebSocket(TEST_SERVER_URL);
    socket.addEventListener('open', done);
  });

  afterEach(function (done) {
    socket.addEventListener('close', done);
    socket.close();
  });

  it('should work with addEventListener', function (done) {
    var parent = window.zone;

    socket.addEventListener('message', function (contents) {
      expect(window.zone.parent).toBe(parent);
      expect(contents.data).toBe('hi');
      done();
    });
    socket.send('hi');
  });

  it('should respect removeEventListener', function (done) {
    var log = '';
    function logOnMessage () {
      log += 'a';

      expect(log).toEqual('a');

      socket.removeEventListener('message', logOnMessage);
      socket.send('hi');

      setTimeout(function () {
        expect(log).toEqual('a');
        done();
      }, 10);
    };

    socket.addEventListener('message', logOnMessage);
    socket.send('hi');
  });

  // TODO(vicb) this test is not working
  // https://github.com/angular/zone.js/issues/81
  xit('should work with onmessage', function (done) {
    var parent = window.zone;
    socket.onmessage = function (contents) {
      expect(window.zone.parent).toBe(parent);
      expect(contents.data).toBe('hi');
      done();
    };
    socket.send('hi');
  });

  it('should only allow one onmessage handler', function (done) {
    var log = '';
    socket.onmessage = function () {
      log += 'a';
      expect(log).toEqual('b');
      done();
    };
    socket.onmessage = function () {
      log += 'b';
      expect(log).toEqual('b');
      done();
    };

    socket.send('hi');
  });

  it('should handle removing onmessage', function (done) {
    var log = '';
    socket.onmessage = function () {
      log += 'a';
    };
    socket.onmessage = null;

    socket.send('hi');

    setTimeout(function() {
      expect(log).toEqual('');
      done();
    }, 500);
  });

});
<|endoftext|>


<commit_before>(function() {
  function getPerfTimestamp() {
    return performance.now();
  }

  var pageStartTime = getPerfTimestamp();
  var bodyLoadTime;
  var pageEndTime;

  function parseQueryParameters() {
    var params = {};
    var r = /([^&=]+)=?([^&]*)/g;

    function d(s) { return decodeURIComponent(s.replace(/\+/g, ' ')); }

    var match;
    while (match = r.exec(window.location.search.substring(1)))
      params[d(match[1])] = d(match[2]);

    return params;
  }

  var testParams;
  function loadTestParams() {
    var queryParameters = parseQueryParameters();
    testParams = {};
    testParams.testType = queryParameters["testType"] || "AV";
    testParams.useAppendStream = (queryParameters["useAppendStream"] == "true");
    testParams.doNotWaitForBodyOnLoad = (queryParameters["doNotWaitForBodyOnLoad"] == "true");
    testParams.startOffset = 0;
    testParams.appendSize = parseInt(queryParameters["appendSize"] || "65536");
    testParams.graphDuration = parseInt(queryParameters["graphDuration"] || "1000");
  }

  function plotTimestamps(timestamps, graphDuration, element) {
    var c = document.getElementById('c');
    var ctx = c.getContext('2d');

    var bars = [
      { label: 'Page Load Total',
        start: pageStartTime,
        end: pageEndTime,
        color: '#404040' },
      { label: 'body.onload Delay',
        start: pageStartTime,
        end: bodyLoadTime,
        color: '#808080' },
      { label: 'Test Total',
        start: timestamps.testStartTime,
        end: timestamps.testEndTime,
        color: '#00FF00' },
      { label: 'MediaSource opening',
        start: timestamps.mediaSourceOpenStartTime,
        end: timestamps.mediaSourceOpenEndTime,
        color: '#008800' }
    ];

    var maxAppendEndTime = 0;
    for (var i = 0; i < timestamps.appenders.length; ++i) {
      var appender = timestamps.appenders[i];
      bars.push({ label: 'XHR',
                  start: appender.xhrStartTime,
                  end: appender.xhrEndTime,
                  color: '#0088FF' });
      bars.push({ label: 'Append',
                  start: appender.appendStartTime,
                  end: appender.appendEndTime,
                  color: '#00FFFF' });
      if (appender.appendEndTime > maxAppendEndTime) {
        maxAppendEndTime = appender.appendEndTime;
      }
    }

    bars.push({label: 'Post Append Delay', start: maxAppendEndTime, end: timestamps.testEndTime, color: '#B0B0B0' });

    var minTimestamp = Number.MAX_VALUE;
    for (var i = 0; i < bars.length; ++i) {
      minTimestamp = Math.min(minTimestamp, bars[i].start);
    }

    var graphWidth = c.width - 100;
    function convertTimestampToX(t) {
      return graphWidth * (t - minTimestamp) / graphDuration;
    }
    var y = 0;
    var barThickness = 20;
    c.height = bars.length * barThickness;
    ctx.font = (0.75 * barThickness) + 'px arial';
    for (var i = 0; i < bars.length; ++i) {
      var bar = bars[i];
      var xStart = convertTimestampToX(bar.start);
      var xEnd = convertTimestampToX(bar.end);
      ctx.fillStyle = bar.color;
      ctx.fillRect(xStart, y, xEnd - xStart, barThickness);

      ctx.fillStyle = 'black';
      var text = bar.label + ' (' + (bar.end - bar.start).toFixed(3) + ' ms)';
      ctx.fillText(text, xEnd + 10, y + (0.75 * barThickness));
      y += barThickness;
    }
    reportTelemetryMediaMetrics(bars, element);
  }

  function displayResults(stats) {
    var statsDiv = document.getElementById('stats');

    if (!stats) {
      statsDiv.innerHTML = "Test failed";
      return;
    }

    var statsMarkup = "Test passed

";
    for (var i in stats) {
      statsMarkup += "
" + i + ":	" + stats[i].toFixed(3) + " ms	";
    }
    statsMarkup += "
";
    statsDiv.innerHTML = statsMarkup;
  }

  function reportTelemetryMediaMetrics(stats, element) {
    if (!stats || !window.__getMediaMetric) {
      console.error("Stats not collected or could not find getMediaMetric().");
      return;
    }
    var metric = window.__getMediaMetric(element);
    if (!metric) {
      console.error("Can not report Telemetry media metrics.");
      return
    }
    for (var i = 0; i < stats.length; ++i) {
      var bar = stats[i];
      var label = bar.label.toLowerCase().replace(/\s+|\./g, '_');
      var value =  (bar.end - bar.start).toFixed(3);
      console.log("appending to telemetry " + label + " : "  + value);
      metric.appendMetric("mse_" + label, value);
    }
  }

  function updateControls(testParams) {
    var testTypeElement = document.getElementById("testType");
    for (var i in testTypeElement.options) {
      var option = testTypeElement.options[i];
      if (option.value == testParams.testType) {
        testTypeElement.selectedIndex = option.index;
      }
    }

    document.getElementById("useAppendStream").checked = testParams.useAppendStream;
    document.getElementById("doNotWaitForBodyOnLoad").checked = testParams.doNotWaitForBodyOnLoad;
    document.getElementById("appendSize").value = testParams.appendSize;
    document.getElementById("graphDuration").value = testParams.graphDuration;
  }

  function BufferAppender(mimetype, url, id, startOffset, appendSize) {
    this.mimetype = mimetype;
    this.url = url;
    this.id = id;
    this.startOffset = startOffset;
    this.appendSize = appendSize;
    this.xhr = new XMLHttpRequest();
    this.sourceBuffer = null;
  }

  BufferAppender.prototype.start = function() {
    this.xhr.addEventListener('loadend', this.onLoadEnd.bind(this));
    this.xhr.open('GET', this.url);
    this.xhr.setRequestHeader('Range', 'bytes=' + this.startOffset + '-' +
                              (this.startOffset + this.appendSize - 1));
    this.xhr.responseType = 'arraybuffer';
    this.xhr.send();

    this.xhrStartTime = getPerfTimestamp();
  };

  BufferAppender.prototype.onLoadEnd = function() {
    this.xhrEndTime = getPerfTimestamp();
    this.attemptAppend();
  };

  BufferAppender.prototype.onSourceOpen = function(mediaSource) {
    if (this.sourceBuffer)
      return;
    this.sourceBuffer = mediaSource.addSourceBuffer(this.mimetype);
  };

  BufferAppender.prototype.attemptAppend = function() {
    if (!this.xhr.response || !this.sourceBuffer)
      return;

    this.appendStartTime = getPerfTimestamp();

    if (this.sourceBuffer.appendBuffer) {
      this.sourceBuffer.addEventListener('updateend',
                                         this.onUpdateEnd.bind(this));
      this.sourceBuffer.appendBuffer(this.xhr.response);
    } else {
      this.sourceBuffer.append(new Uint8Array(this.xhr.response));
      this.appendEndTime = getPerfTimestamp();
    }

    this.xhr = null;
  };

  BufferAppender.prototype.onUpdateEnd = function() {
    this.appendEndTime = getPerfTimestamp();
  };

  BufferAppender.prototype.onPlaybackStarted = function() {
    var now = getPerfTimestamp();
    this.playbackStartTime = now;
    if (this.sourceBuffer.updating) {
      // Still appending but playback has already started so just abort the XHR
      // and append.
      this.sourceBuffer.abort();
      this.xhr.abort();
    }
  };

  BufferAppender.prototype.getXHRLoadDuration = function() {
    return this.xhrEndTime - this.xhrStartTime;
  };

  BufferAppender.prototype.getAppendDuration = function() {
    return this.appendEndTime - this.appendStartTime;
  };

  function StreamAppender(mimetype, url, id, startOffset, appendSize) {
    this.mimetype = mimetype;
    this.url = url;
    this.id = id;
    this.startOffset = startOffset;
    this.appendSize = appendSize;
    this.xhr = new XMLHttpRequest();
    this.sourceBuffer = null;
    this.appendStarted = false;
  }

  StreamAppender.prototype.start = function() {
    this.xhr.addEventListener('readystatechange',
                              this.attemptAppend.bind(this));
    this.xhr.addEventListener('loadend', this.onLoadEnd.bind(this));
    this.xhr.open('GET', this.url);
    this.xhr.setRequestHeader('Range', 'bytes=' + this.startOffset + '-' +
                              (this.startOffset + this.appendSize - 1));
    this.xhr.responseType = 'stream';
    if (this.xhr.responseType != 'stream') {
      throw "XHR does not support 'stream' responses.";
    }
    this.xhr.send();

    this.xhrStartTime = getPerfTimestamp();
  };

  StreamAppender.prototype.onLoadEnd = function() {
    this.xhrEndTime = getPerfTimestamp();
    this.attemptAppend();
  };

  StreamAppender.prototype.onSourceOpen = function(mediaSource) {
    if (this.sourceBuffer)
      return;
    this.sourceBuffer = mediaSource.addSourceBuffer(this.mimetype);
  };

  StreamAppender.prototype.attemptAppend = function() {
    if (this.xhr.readyState < this.xhr.LOADING) {
      return;
    }

    if (!this.xhr.response || !this.sourceBuffer || this.appendStarted)
      return;

    this.appendStartTime = getPerfTimestamp();
    this.appendStarted = true;
    this.sourceBuffer.addEventListener('updateend',
                                       this.onUpdateEnd.bind(this));
    this.sourceBuffer.appendStream(this.xhr.response);
  };

  StreamAppender.prototype.onUpdateEnd = function() {
    this.appendEndTime = getPerfTimestamp();
  };

  StreamAppender.prototype.onPlaybackStarted = function() {
    var now = getPerfTimestamp();
    this.playbackStartTime = now;
    if (this.sourceBuffer.updating) {
      // Still appending but playback has already started so just abort the XHR
      // and append.
      this.sourceBuffer.abort();
      this.xhr.abort();
      if (!this.appendEndTime)
        this.appendEndTime = now;

      if (!this.xhrEndTime)
        this.xhrEndTime = now;
    }
  };

  StreamAppender.prototype.getXHRLoadDuration = function() {
    return this.xhrEndTime - this.xhrStartTime;
  };

  StreamAppender.prototype.getAppendDuration = function() {
    return this.appendEndTime - this.appendStartTime;
  };

  // runAppendTest() sets testDone to true once all appends finish.
  var testDone = false;
  function runAppendTest(mediaElement, appenders, doneCallback) {
    var testStartTime = getPerfTimestamp();
    var mediaSourceOpenStartTime;
    var mediaSourceOpenEndTime;

    for (var i = 0; i < appenders.length; ++i) {
      appenders[i].start();
    }

    function onSourceOpen(event) {
      var mediaSource = event.target;

      mediaSourceOpenEndTime = getPerfTimestamp();

      for (var i = 0; i < appenders.length; ++i) {
        appenders[i].onSourceOpen(mediaSource);
      }

      for (var i = 0; i < appenders.length; ++i) {
        appenders[i].attemptAppend(mediaSource);
      }

      mediaElement.play();
    }

    var mediaSource;
    if (window['MediaSource']) {
      mediaSource = new window.MediaSource();
      mediaSource.addEventListener('sourceopen', onSourceOpen);
    } else {
      mediaSource = new window.WebKitMediaSource();
      mediaSource.addEventListener('webkitsourceopen', onSourceOpen);
    }

    var listener;
    var timeout;
    function checkForCurrentTimeChange() {
      if (testDone)
        return;

      if (mediaElement.readyState < mediaElement.HAVE_METADATA ||
          mediaElement.currentTime <= 0)
        return;

      for (var i = 0; i < appenders.length; ++i) {
        appenders[i].onPlaybackStarted(mediaSource);
      }

      var testEndTime = getPerfTimestamp();

      testDone = true;
      window.clearInterval(listener);
      window.clearTimeout(timeout);

      var stats = {};
      stats.total = testEndTime - testStartTime;
      stats.sourceOpen = mediaSourceOpenEndTime - mediaSourceOpenStartTime;
      stats.maxXHRLoadDuration = appenders[0].getXHRLoadDuration();
      stats.maxAppendDuration = appenders[0].getAppendDuration();

      var timestamps = {};
      timestamps.testStartTime = testStartTime;
      timestamps.testEndTime = testEndTime;
      timestamps.mediaSourceOpenStartTime = mediaSourceOpenStartTime;
      timestamps.mediaSourceOpenEndTime = mediaSourceOpenEndTime;
      timestamps.appenders = [];

      for (var i = 1; i < appenders.length; ++i) {
        var appender = appenders[i];
        var xhrLoadDuration = appender.getXHRLoadDuration();
        var appendDuration = appender.getAppendDuration();

        if (xhrLoadDuration > stats.maxXHRLoadDuration)
          stats.maxXHRLoadDuration = xhrLoadDuration;

        if (appendDuration > stats.maxAppendDuration)
          stats.maxAppendDuration = appendDuration;
      }

      for (var i = 0; i < appenders.length; ++i) {
        var appender = appenders[i];
        var appenderTimestamps = {};
        appenderTimestamps.xhrStartTime = appender.xhrStartTime;
        appenderTimestamps.xhrEndTime = appender.xhrEndTime;
        appenderTimestamps.appendStartTime = appender.appendStartTime;
        appenderTimestamps.appendEndTime = appender.appendEndTime;
        appenderTimestamps.playbackStartTime = appender.playbackStartTime;
        timestamps.appenders.push(appenderTimestamps);
      }

      mediaElement.pause();

      pageEndTime = getPerfTimestamp();
      doneCallback(stats, timestamps);
    };

    mediaElement.addEventListener('timeupdate', checkForCurrentTimeChange);

    listener = setInterval(checkForCurrentTimeChange, 15);
    timeout = setTimeout(function() {
      if (testDone)
        return;

      console.log('Test timed out.');
      testDone = true;
      window.clearInterval(listener);

      mediaElement.pause();
      doneCallback(null);
    }, 10000);

    mediaSourceOpenStartTime = getPerfTimestamp();
    mediaElement.src = URL.createObjectURL(mediaSource);
  };

  function onBodyLoad() {
    bodyLoadTime = getPerfTimestamp();

    if (!testParams.doNotWaitForBodyOnLoad) {
      startTest();
    }
  }

  function startTest() {
    updateControls(testParams);

    var appenders = [];

    if (useAppendStream && !window.MediaSource)
      throw "Can't use appendStream() because the unprefixed MediaSource object is not present.";

    var Appender = testParams.useAppendStream ? StreamAppender : BufferAppender;

    if (testParams.testType.indexOf("A") != -1) {
      appenders.push(new Appender("audio/mp4; codecs=\"mp4a.40.2\"", "audio.mp4", "a", testParams.startOffset, testParams.appendSize));
    }

    if (testParams.testType.indexOf("V") != -1) {
      appenders.push(new Appender("video/mp4; codecs=\"avc1.640028\"", "video.mp4", "v", testParams.startOffset, testParams.appendSize));
    }

    var video = document.getElementById('v');
    video.id = getTestID();
    runAppendTest(video, appenders, function(stats, timestamps) {
      displayResults(stats);
      plotTimestamps(timestamps, testParams.graphDuration, video);
    });
  }

  function getTestID() {
    console.log("setting test ID")
    console.log(testParams.doNotWaitForBodyOnLoad)
    var id = testParams.testType;
    if (testParams.useAppendStream)
      id += "_stream"
    else
      id += "_buffer"
    if (testParams.doNotWaitForBodyOnLoad)
      id += "_pre_load"
    else
      id += "_post_load"
    return id;
  }

  function setupTest() {
    loadTestParams();
    document.body.onload = onBodyLoad;

    if (testParams.doNotWaitForBodyOnLoad) {
      startTest();
    }
  }

  window["setupTest"] = setupTest;
  window.__testDone = function() {
    return testDone;
  };
})();
<commit_msg>Fix time when MSE perf tests are marked done.<commit_after>(function() {
  function getPerfTimestamp() {
    return performance.now();
  }

  var pageStartTime = getPerfTimestamp();
  var bodyLoadTime;
  var pageEndTime;

  function parseQueryParameters() {
    var params = {};
    var r = /([^&=]+)=?([^&]*)/g;

    function d(s) { return decodeURIComponent(s.replace(/\+/g, ' ')); }

    var match;
    while (match = r.exec(window.location.search.substring(1)))
      params[d(match[1])] = d(match[2]);

    return params;
  }

  var testParams;
  function loadTestParams() {
    var queryParameters = parseQueryParameters();
    testParams = {};
    testParams.testType = queryParameters["testType"] || "AV";
    testParams.useAppendStream = (queryParameters["useAppendStream"] == "true");
    testParams.doNotWaitForBodyOnLoad = (queryParameters["doNotWaitForBodyOnLoad"] == "true");
    testParams.startOffset = 0;
    testParams.appendSize = parseInt(queryParameters["appendSize"] || "65536");
    testParams.graphDuration = parseInt(queryParameters["graphDuration"] || "1000");
  }

  function plotTimestamps(timestamps, graphDuration, element) {
    var c = document.getElementById('c');
    var ctx = c.getContext('2d');

    var bars = [
      { label: 'Page Load Total',
        start: pageStartTime,
        end: pageEndTime,
        color: '#404040' },
      { label: 'body.onload Delay',
        start: pageStartTime,
        end: bodyLoadTime,
        color: '#808080' },
      { label: 'Test Total',
        start: timestamps.testStartTime,
        end: timestamps.testEndTime,
        color: '#00FF00' },
      { label: 'MediaSource opening',
        start: timestamps.mediaSourceOpenStartTime,
        end: timestamps.mediaSourceOpenEndTime,
        color: '#008800' }
    ];

    var maxAppendEndTime = 0;
    for (var i = 0; i < timestamps.appenders.length; ++i) {
      var appender = timestamps.appenders[i];
      bars.push({ label: 'XHR',
                  start: appender.xhrStartTime,
                  end: appender.xhrEndTime,
                  color: '#0088FF' });
      bars.push({ label: 'Append',
                  start: appender.appendStartTime,
                  end: appender.appendEndTime,
                  color: '#00FFFF' });
      if (appender.appendEndTime > maxAppendEndTime) {
        maxAppendEndTime = appender.appendEndTime;
      }
    }

    bars.push({label: 'Post Append Delay', start: maxAppendEndTime, end: timestamps.testEndTime, color: '#B0B0B0' });

    var minTimestamp = Number.MAX_VALUE;
    for (var i = 0; i < bars.length; ++i) {
      minTimestamp = Math.min(minTimestamp, bars[i].start);
    }

    var graphWidth = c.width - 100;
    function convertTimestampToX(t) {
      return graphWidth * (t - minTimestamp) / graphDuration;
    }
    var y = 0;
    var barThickness = 20;
    c.height = bars.length * barThickness;
    ctx.font = (0.75 * barThickness) + 'px arial';
    for (var i = 0; i < bars.length; ++i) {
      var bar = bars[i];
      var xStart = convertTimestampToX(bar.start);
      var xEnd = convertTimestampToX(bar.end);
      ctx.fillStyle = bar.color;
      ctx.fillRect(xStart, y, xEnd - xStart, barThickness);

      ctx.fillStyle = 'black';
      var text = bar.label + ' (' + (bar.end - bar.start).toFixed(3) + ' ms)';
      ctx.fillText(text, xEnd + 10, y + (0.75 * barThickness));
      y += barThickness;
    }
    reportTelemetryMediaMetrics(bars, element);
  }

  function displayResults(stats) {
    var statsDiv = document.getElementById('stats');

    if (!stats) {
      statsDiv.innerHTML = "Test failed";
      return;
    }

    var statsMarkup = "Test passed

";
    for (var i in stats) {
      statsMarkup += "
" + i + ":	" + stats[i].toFixed(3) + " ms	";
    }
    statsMarkup += "
";
    statsDiv.innerHTML = statsMarkup;
  }

  function reportTelemetryMediaMetrics(stats, element) {
    if (!stats || !window.__getMediaMetric) {
      console.error("Stats not collected or could not find getMediaMetric().");
      return;
    }
    var metric = window.__getMediaMetric(element);
    if (!metric) {
      console.error("Can not report Telemetry media metrics.");
      return
    }
    for (var i = 0; i < stats.length; ++i) {
      var bar = stats[i];
      var label = bar.label.toLowerCase().replace(/\s+|\./g, '_');
      var value =  (bar.end - bar.start).toFixed(3);
      console.log("appending to telemetry " + label + " : "  + value);
      metric.appendMetric("mse_" + label, value);
    }
  }

  function updateControls(testParams) {
    var testTypeElement = document.getElementById("testType");
    for (var i in testTypeElement.options) {
      var option = testTypeElement.options[i];
      if (option.value == testParams.testType) {
        testTypeElement.selectedIndex = option.index;
      }
    }

    document.getElementById("useAppendStream").checked = testParams.useAppendStream;
    document.getElementById("doNotWaitForBodyOnLoad").checked = testParams.doNotWaitForBodyOnLoad;
    document.getElementById("appendSize").value = testParams.appendSize;
    document.getElementById("graphDuration").value = testParams.graphDuration;
  }

  function BufferAppender(mimetype, url, id, startOffset, appendSize) {
    this.mimetype = mimetype;
    this.url = url;
    this.id = id;
    this.startOffset = startOffset;
    this.appendSize = appendSize;
    this.xhr = new XMLHttpRequest();
    this.sourceBuffer = null;
  }

  BufferAppender.prototype.start = function() {
    this.xhr.addEventListener('loadend', this.onLoadEnd.bind(this));
    this.xhr.open('GET', this.url);
    this.xhr.setRequestHeader('Range', 'bytes=' + this.startOffset + '-' +
                              (this.startOffset + this.appendSize - 1));
    this.xhr.responseType = 'arraybuffer';
    this.xhr.send();

    this.xhrStartTime = getPerfTimestamp();
  };

  BufferAppender.prototype.onLoadEnd = function() {
    this.xhrEndTime = getPerfTimestamp();
    this.attemptAppend();
  };

  BufferAppender.prototype.onSourceOpen = function(mediaSource) {
    if (this.sourceBuffer)
      return;
    this.sourceBuffer = mediaSource.addSourceBuffer(this.mimetype);
  };

  BufferAppender.prototype.attemptAppend = function() {
    if (!this.xhr.response || !this.sourceBuffer)
      return;

    this.appendStartTime = getPerfTimestamp();

    if (this.sourceBuffer.appendBuffer) {
      this.sourceBuffer.addEventListener('updateend',
                                         this.onUpdateEnd.bind(this));
      this.sourceBuffer.appendBuffer(this.xhr.response);
    } else {
      this.sourceBuffer.append(new Uint8Array(this.xhr.response));
      this.appendEndTime = getPerfTimestamp();
    }

    this.xhr = null;
  };

  BufferAppender.prototype.onUpdateEnd = function() {
    this.appendEndTime = getPerfTimestamp();
  };

  BufferAppender.prototype.onPlaybackStarted = function() {
    var now = getPerfTimestamp();
    this.playbackStartTime = now;
    if (this.sourceBuffer.updating) {
      // Still appending but playback has already started so just abort the XHR
      // and append.
      this.sourceBuffer.abort();
      this.xhr.abort();
    }
  };

  BufferAppender.prototype.getXHRLoadDuration = function() {
    return this.xhrEndTime - this.xhrStartTime;
  };

  BufferAppender.prototype.getAppendDuration = function() {
    return this.appendEndTime - this.appendStartTime;
  };

  function StreamAppender(mimetype, url, id, startOffset, appendSize) {
    this.mimetype = mimetype;
    this.url = url;
    this.id = id;
    this.startOffset = startOffset;
    this.appendSize = appendSize;
    this.xhr = new XMLHttpRequest();
    this.sourceBuffer = null;
    this.appendStarted = false;
  }

  StreamAppender.prototype.start = function() {
    this.xhr.addEventListener('readystatechange',
                              this.attemptAppend.bind(this));
    this.xhr.addEventListener('loadend', this.onLoadEnd.bind(this));
    this.xhr.open('GET', this.url);
    this.xhr.setRequestHeader('Range', 'bytes=' + this.startOffset + '-' +
                              (this.startOffset + this.appendSize - 1));
    this.xhr.responseType = 'stream';
    if (this.xhr.responseType != 'stream') {
      throw "XHR does not support 'stream' responses.";
    }
    this.xhr.send();

    this.xhrStartTime = getPerfTimestamp();
  };

  StreamAppender.prototype.onLoadEnd = function() {
    this.xhrEndTime = getPerfTimestamp();
    this.attemptAppend();
  };

  StreamAppender.prototype.onSourceOpen = function(mediaSource) {
    if (this.sourceBuffer)
      return;
    this.sourceBuffer = mediaSource.addSourceBuffer(this.mimetype);
  };

  StreamAppender.prototype.attemptAppend = function() {
    if (this.xhr.readyState < this.xhr.LOADING) {
      return;
    }

    if (!this.xhr.response || !this.sourceBuffer || this.appendStarted)
      return;

    this.appendStartTime = getPerfTimestamp();
    this.appendStarted = true;
    this.sourceBuffer.addEventListener('updateend',
                                       this.onUpdateEnd.bind(this));
    this.sourceBuffer.appendStream(this.xhr.response);
  };

  StreamAppender.prototype.onUpdateEnd = function() {
    this.appendEndTime = getPerfTimestamp();
  };

  StreamAppender.prototype.onPlaybackStarted = function() {
    var now = getPerfTimestamp();
    this.playbackStartTime = now;
    if (this.sourceBuffer.updating) {
      // Still appending but playback has already started so just abort the XHR
      // and append.
      this.sourceBuffer.abort();
      this.xhr.abort();
      if (!this.appendEndTime)
        this.appendEndTime = now;

      if (!this.xhrEndTime)
        this.xhrEndTime = now;
    }
  };

  StreamAppender.prototype.getXHRLoadDuration = function() {
    return this.xhrEndTime - this.xhrStartTime;
  };

  StreamAppender.prototype.getAppendDuration = function() {
    return this.appendEndTime - this.appendStartTime;
  };

  // runAppendTest() sets testDone to true once all appends finish.
  var testDone = false;
  function runAppendTest(mediaElement, appenders, doneCallback) {
    var testStartTime = getPerfTimestamp();
    var mediaSourceOpenStartTime;
    var mediaSourceOpenEndTime;

    for (var i = 0; i < appenders.length; ++i) {
      appenders[i].start();
    }

    function onSourceOpen(event) {
      var mediaSource = event.target;

      mediaSourceOpenEndTime = getPerfTimestamp();

      for (var i = 0; i < appenders.length; ++i) {
        appenders[i].onSourceOpen(mediaSource);
      }

      for (var i = 0; i < appenders.length; ++i) {
        appenders[i].attemptAppend(mediaSource);
      }

      mediaElement.play();
    }

    var mediaSource;
    if (window['MediaSource']) {
      mediaSource = new window.MediaSource();
      mediaSource.addEventListener('sourceopen', onSourceOpen);
    } else {
      mediaSource = new window.WebKitMediaSource();
      mediaSource.addEventListener('webkitsourceopen', onSourceOpen);
    }

    var listener;
    var timeout;
    function checkForCurrentTimeChange() {
      if (testDone)
        return;

      if (mediaElement.readyState < mediaElement.HAVE_METADATA ||
          mediaElement.currentTime <= 0)
        return;

      for (var i = 0; i < appenders.length; ++i) {
        appenders[i].onPlaybackStarted(mediaSource);
      }

      var testEndTime = getPerfTimestamp();

      testDone = true;
      window.clearInterval(listener);
      window.clearTimeout(timeout);

      var stats = {};
      stats.total = testEndTime - testStartTime;
      stats.sourceOpen = mediaSourceOpenEndTime - mediaSourceOpenStartTime;
      stats.maxXHRLoadDuration = appenders[0].getXHRLoadDuration();
      stats.maxAppendDuration = appenders[0].getAppendDuration();

      var timestamps = {};
      timestamps.testStartTime = testStartTime;
      timestamps.testEndTime = testEndTime;
      timestamps.mediaSourceOpenStartTime = mediaSourceOpenStartTime;
      timestamps.mediaSourceOpenEndTime = mediaSourceOpenEndTime;
      timestamps.appenders = [];

      for (var i = 1; i < appenders.length; ++i) {
        var appender = appenders[i];
        var xhrLoadDuration = appender.getXHRLoadDuration();
        var appendDuration = appender.getAppendDuration();

        if (xhrLoadDuration > stats.maxXHRLoadDuration)
          stats.maxXHRLoadDuration = xhrLoadDuration;

        if (appendDuration > stats.maxAppendDuration)
          stats.maxAppendDuration = appendDuration;
      }

      for (var i = 0; i < appenders.length; ++i) {
        var appender = appenders[i];
        var appenderTimestamps = {};
        appenderTimestamps.xhrStartTime = appender.xhrStartTime;
        appenderTimestamps.xhrEndTime = appender.xhrEndTime;
        appenderTimestamps.appendStartTime = appender.appendStartTime;
        appenderTimestamps.appendEndTime = appender.appendEndTime;
        appenderTimestamps.playbackStartTime = appender.playbackStartTime;
        timestamps.appenders.push(appenderTimestamps);
      }

      mediaElement.pause();

      pageEndTime = getPerfTimestamp();
      doneCallback(stats, timestamps);
    };

    mediaElement.addEventListener('timeupdate', checkForCurrentTimeChange);

    listener = setInterval(checkForCurrentTimeChange, 15);
    timeout = setTimeout(function() {
      if (testDone)
        return;

      console.log('Test timed out.');
      testDone = true;
      window.clearInterval(listener);

      mediaElement.pause();
      doneCallback(null);
    }, 10000);

    mediaSourceOpenStartTime = getPerfTimestamp();
    mediaElement.src = URL.createObjectURL(mediaSource);
  };

  function onBodyLoad() {
    bodyLoadTime = getPerfTimestamp();

    if (!testParams.doNotWaitForBodyOnLoad) {
      startTest();
    }
  }

  function startTest() {
    updateControls(testParams);

    var appenders = [];

    if (useAppendStream && !window.MediaSource)
      throw "Can't use appendStream() because the unprefixed MediaSource object is not present.";

    var Appender = testParams.useAppendStream ? StreamAppender : BufferAppender;

    if (testParams.testType.indexOf("A") != -1) {
      appenders.push(new Appender("audio/mp4; codecs=\"mp4a.40.2\"", "audio.mp4", "a", testParams.startOffset, testParams.appendSize));
    }

    if (testParams.testType.indexOf("V") != -1) {
      appenders.push(new Appender("video/mp4; codecs=\"avc1.640028\"", "video.mp4", "v", testParams.startOffset, testParams.appendSize));
    }

    var video = document.getElementById('v');
    video.id = getTestID();
    runAppendTest(video, appenders, function(stats, timestamps) {
      displayResults(stats);
      plotTimestamps(timestamps, testParams.graphDuration, video);
      window.__testDone = true;
    });
  }

  function getTestID() {
    console.log("setting test ID")
    console.log(testParams.doNotWaitForBodyOnLoad)
    var id = testParams.testType;
    if (testParams.useAppendStream)
      id += "_stream"
    else
      id += "_buffer"
    if (testParams.doNotWaitForBodyOnLoad)
      id += "_pre_load"
    else
      id += "_post_load"
    return id;
  }

  function setupTest() {
    loadTestParams();
    document.body.onload = onBodyLoad;

    if (testParams.doNotWaitForBodyOnLoad) {
      startTest();
    }
  }

  window["setupTest"] = setupTest;
  window.__testDone = false;
})();
<|endoftext|>


<commit_before>/**
 * Created by dmitri on 3/30/2017.
 */
'use strict';

import chai from 'chai';
import chaiAsPromised from 'chai-as-promised';
const expect = chai.expect;
chai.should();
chai.use(chaiAsPromised);

import Handler from '../src/handler/handler';
import PromiseHandler from '../src/handler/promise';
import Pipe from '../src/pipe';

describe('PromiseHandler', function()
{
  const pipe = new Pipe();
  const beg = new Handler();
  const end = new Handler();
  const prh = new PromiseHandler();
  pipe.addEnd(beg).addEnd(prh).addEnd(end);
  function simpleAcceptor(cntx, accept)
  {
    accept(cntx.process.message);
  }
  function complexAcceptor(cntx, accept)
  {
    if(!this.results)
      this.results = [];
    this.results.push(cntx.process.message);
    if(this.results.length == 2)
      accept(this.results);
  }
  it('inbound promise resolves on inbound process', function()
  {
    const msg = 'Foo';
    const promise = prh.newInboundPromise(simpleAcceptor, 'Inbound');
    pipe.processInbound(msg);
    //prh.delPromise('Inbound', msg);
    return expect(promise).to.eventually.equal(msg);
  });
  it('inbound promise doesn\'t resolve on outbound process', function()
  {
    const msg = 'Foo';
    const promise = prh.newInboundPromise(simpleAcceptor, 'Inbound');
    pipe.processOutbound(msg);
    prh.delPromise('Inbound', msg);
    return promise.should.be.rejectedWith(msg);
  });
  it('outbound promise resolves on outbound process', function()
  {
    const msg = 'Bar';
    const promise = prh.newOutboundPromise(simpleAcceptor, 'Outbound');
    pipe.processOutbound(msg);
    //prh.delPromise('Outbound', msg);
    return expect(promise).to.eventually.equal(msg);
  });
  it('outbound promise doesn\'t resolve on inbound process', function()
  {
    const msg = 'Bar';
    const promise = prh.newOutboundPromise(simpleAcceptor, 'Outbound');
    pipe.processInbound(msg);
    prh.delPromise('Outbound', msg);
    return promise.should.be.rejectedWith(msg);
  });
  it('unidirectional promise works', function()
  {
    const msgIn = 'Foo';
    const msgOut = 'Bar';
    const promise = prh.newPromise(complexAcceptor.bind({}));
    pipe.submitInbound(msgIn).submitOutbound(msgOut);
    return expect(promise).to.eventually.deep.equal([msgIn, msgOut]);
  });
});

<commit_msg>test(handler): Updated PromiseHandler tests<commit_after>/**
 * Created by dmitri on 3/30/2017.
 */
'use strict';

import chai from 'chai';
import chaiAsPromised from 'chai-as-promised';
const expect = chai.expect;
chai.should();
chai.use(chaiAsPromised);

import Handler from '../src/handler/handler';
import PromiseHandler from '../src/handler/promise';
import Pipe from '../src/pipe';

describe('PromiseHandler', function()
{
  const pipe = new Pipe();
  const beg = new Handler();
  const end = new Handler();
  const prh = new PromiseHandler();
  pipe.addEnd(beg).addEnd(prh).addEnd(end);
  function simpleAcceptor(cntx, accept)
  {
    accept(cntx.process.message);
  }
  function complexAcceptor(cntx, accept)
  {
    if(!this.results)
      this.results = [];
    this.results.push(cntx.process.message);
    if(this.results.length === 2)
      accept(this.results);
  }
  it('inbound promise resolves on inbound process', function()
  {
    const msg = 'Foo';
    const promise = prh.newInboundPromise(simpleAcceptor, 'Inbound');
    pipe.processInbound(msg);
    expect(prh.getPromise('Inbound')).to.not.be.ok;
    return expect(promise).to.eventually.equal(msg);
  });
  it('inbound promise doesn\'t resolve on outbound process', function()
  {
    const msg = 'Foo';
    const promise = prh.newInboundPromise(simpleAcceptor, 'Inbound');
    pipe.processOutbound(msg);
    expect(prh.getPromise('Inbound')).to.equal(promise);
    prh.delPromise('Inbound', msg);
    return promise.should.be.rejectedWith(msg);
  });
  it('outbound promise resolves on outbound process', function()
  {
    const msg = 'Bar';
    const promise = prh.newOutboundPromise(simpleAcceptor, 'Outbound');
    pipe.processOutbound(msg);
    expect(prh.getPromise('Outbound')).to.not.be.ok;
    return expect(promise).to.eventually.equal(msg);
  });
  it('outbound promise doesn\'t resolve on inbound process', function()
  {
    const msg = 'Bar';
    const promise = prh.newOutboundPromise(simpleAcceptor, 'Outbound');
    pipe.processInbound(msg);
    expect(prh.getPromise('Outbound')).to.equal(promise);
    prh.delPromise('Outbound', msg);
    return promise.should.be.rejectedWith(msg);
  });
  it('unidirectional promise works', function()
  {
    const msgIn = 'Foo';
    const msgOut = 'Bar';
    const promise = prh.newPromise(complexAcceptor);
    pipe.submitInbound(msgIn).submitOutbound(msgOut);
    expect(prh.promise).to.equal(promise);
    const res = expect(promise).to.eventually.deep.equal([msgIn, msgOut]);
    return res.then(() => expect(prh.promise).to.not.be.ok);
  });
});

<|endoftext|>