<commit_before>// ==UserScript==
// @author      PotcFdk
// @name        Archive.org AutoSaver
// @namespace   https://github.com/PotcFdk/ArchiveOrgAutoSaver
// @description Auto-saves all missing pages that you might encounter.
// @include     https://web.archive.org/web/*
// @version     0.0.5
// @grant       none
// @downloadURL https://raw.githubusercontent.com/PotcFdk/ArchiveOrgAutoSaver/master/Archive.org_AutoSaver.user.js
// @updateURL https://raw.githubusercontent.com/PotcFdk/ArchiveOrgAutoSaver/master/Archive.org_AutoSaver.meta.js
// ==/UserScript==

/*
	Archive.org AutoSaver - Copyright (c) PotcFdk, 2015

	Licensed under the Apache License, Version 2.0 (the "License");
	you may not use this file except in compliance with the License.
	You may obtain a copy of the License at

	http://www.apache.org/licenses/LICENSE-2.0

	Unless required by applicable law or agreed to in writing, software
	distributed under the License is distributed on an "AS IS" BASIS,
	WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
	See the License for the specific language governing permissions and
	limitations under the License.
*/

var livewebInfo = document.getElementById('livewebInfo');
if (livewebInfo) {
	var saveLink = livewebInfo.getElementsByTagName ("a");
	if (saveLink && saveLink[0] && saveLink[0].textContent
		&& saveLink[0].textContent == "Save this url in the Wayback Machine")
	{
		console.log ("[Archive.org AutoSaver] Detected missing page. Redirecting...");
		window.location.href = saveLink[0].href;
	}
}

var error = document.getElementById ('error');
var form1 = document.getElementsByName ('form1');
if (error && error.textContent
	&& (error.textContent.indexOf ('Wayback Machine doesn\'t have that page archived.') != -1
		|| error.textContent.indexOf ('This url is not available on the live web or can not be archived.') != -1)
	&& form1 && form1[0])
{
	var reqUrlInput = form1[0].getElementsByTagName ('input');
	if (reqUrlInput && reqUrlInput[0] && reqUrlInput[0].value)
	{
		error.innerHTML = error.innerHTML
			+ '
[Archive.Org AutoSaver] Click here to try to save this url anyways.
';
		console.log ("[Archive.org AutoSaver] Detected not available page. Added save link.");
	}
}

var recordDoneClose = document.getElementById ('__wb_record_done_close')
if (recordDoneClose && __on_wb_record_done_close) {
	__on_wb_record_done_close();
	console.log ("[Archive.org AutoSaver] Closed 'Page saved as' window.");
}<commit_msg>Speed up the redirect.<commit_after>// ==UserScript==
// @author      PotcFdk
// @name        Archive.org AutoSaver
// @namespace   https://github.com/PotcFdk/ArchiveOrgAutoSaver
// @description Auto-saves all missing pages that you might encounter.
// @include     https://web.archive.org/web/*
// @version     0.0.5
// @grant       none
// @downloadURL https://raw.githubusercontent.com/PotcFdk/ArchiveOrgAutoSaver/master/Archive.org_AutoSaver.user.js
// @updateURL https://raw.githubusercontent.com/PotcFdk/ArchiveOrgAutoSaver/master/Archive.org_AutoSaver.meta.js
// ==/UserScript==

/*
	Archive.org AutoSaver - Copyright (c) PotcFdk, 2015

	Licensed under the Apache License, Version 2.0 (the "License");
	you may not use this file except in compliance with the License.
	You may obtain a copy of the License at

	http://www.apache.org/licenses/LICENSE-2.0

	Unless required by applicable law or agreed to in writing, software
	distributed under the License is distributed on an "AS IS" BASIS,
	WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
	See the License for the specific language governing permissions and
	limitations under the License.
*/

var livewebInfo = document.getElementById('livewebInfo');
if (livewebInfo) {
	var saveLink = livewebInfo.getElementsByTagName ("a");
	if (saveLink && saveLink[0] && saveLink[0].textContent
		&& saveLink[0].textContent == "Save this url in the Wayback Machine")
	{
		console.log ("[Archive.org AutoSaver] Detected missing page. Redirecting...");
		window.location.href = saveLink[0].href;
	}
}

var error = document.getElementById ('error');
var form1 = document.getElementsByName ('form1');
if (error && error.textContent
	&& (error.textContent.indexOf ('Wayback Machine doesn\'t have that page archived.') != -1
		|| error.textContent.indexOf ('This url is not available on the live web or can not be archived.') != -1)
	&& form1 && form1[0])
{
	var reqUrlInput = form1[0].getElementsByTagName ('input');
	if (reqUrlInput && reqUrlInput[0] && reqUrlInput[0].value)
	{
		error.innerHTML = error.innerHTML
			+ '
[Archive.Org AutoSaver] Click here to try to save this url anyways.
';
		console.log ("[Archive.org AutoSaver] Detected not available page. Added save link.");
	}
}

var recordDoneClose = document.getElementById ('__wb_record_done_close')
if (recordDoneClose && __on_wb_record_done_close) {
	__on_wb_record_done_close();
	console.log ("[Archive.org AutoSaver] Closed 'Page saved as' window.");
}

if (go && {}.toString.call (go) == '[object Function]') {
	console.log ("[Archive.org AutoSaver] Speeding up redirect...");
	window.setTimeout (go, 500);
}
<|endoftext|>


<commit_before>/**
 * Module dependencies
 */

var actionUtil = require( './_util/actionUtil' );
var util = require( 'util' );
var _ = require( 'lodash' );

/**
 * Update One Record
 *
 * An API call to update a model instance with the specified `id`,
 * treating the other unbound parameters as attributes.
 *
 * @param {Integer|String} id  - the unique id of the particular record you'd like to update  (Note: this param should be specified even if primary key is not `id`!!)
 * @param *                    - values to set on the record
 *
 */
module.exports = function updateOneRecord( req, res ) {

	// Look up the model
	var Model = actionUtil.parseModel( req );

	// Locate and validate the required `id` parameter.
	var pk = actionUtil.requirePk( req );

	// Create `values` object (monolithic combination of all parameters)
	// But omit the blacklisted params (like JSONP callback param, etc.)
	var values = actionUtil.parseValues( req, Model );

	// Omit the path parameter `id` from values, unless it was explicitly defined
	// elsewhere (body/query):
	// var idParamExplicitlyIncluded = ( ( req.body && req.body.id ) || req.query.id );
	// if ( !idParamExplicitlyIncluded ) delete values.id;

	// Look up the association configuration and determine how to populate the query
	// @todo support request driven selection of includes/populate
	var associations = actionUtil.getAssociationConfiguration( Model, "detail" );

	// Find and update the targeted record.
	//
	// (Note: this could be achieved in a single query, but a separate `findOne`
	//  is used first to provide a better experience for front-end developers
	//  integrating with the blueprint API.)
	Model.findOne( pk ).exec( function found( err, matchingRecord ) {
		if ( err ) return res.serverError( err );
		if ( !matchingRecord ) return res.notFound();

		Model.update( pk, values ).exec( function updated( err, records ) {

			// Differentiate between waterline-originated validation errors
			// and serious underlying issues. Respond with badRequest if a
			// validation error is encountered, w/ validation info.
			if ( err ) return res.negotiate( err );

			// Because this should only update a single record and update
			// returns an array, just use the first item.  If more than one
			// record was returned, something is amiss.
			if ( !records || !records.length || records.length > 1 ) {
				req._sails.log.warn(
					util.format( 'Unexpected output from `%s.update`.', Model.globalId )
				);
			}

			var updatedRecord = records[ 0 ];

			// If we have the pubsub hook, use the Model's publish method
			// to notify all subscribers about the update.
			if ( req._sails.hooks.pubsub ) {
				if ( req.isSocket ) {
					Model.subscribe( req, records );
				}
				Model.publishUpdate( pk, _.cloneDeep( values ), !req.options.mirror && req, {
					previous: matchingRecord.toJSON()
				} );
			}

			// Do a final query to populate the associations of the record.
			var Q = Model.findOne( updatedRecord[ Model.primaryKey ] );
			Q = actionUtil.populateRecords( Q, associations );
			Q.exec( function foundAgain( err, populatedRecord ) {
				if ( err ) return res.serverError( err );
				if ( !populatedRecord ) return res.serverError( 'Could not find record after updating!' );

				actionUtil.populateIndexes( Model, matchingRecord.id, associations, function ( err, associated ) {

					res.ok( Ember.buildResponse( Model, populatedRecord, associations, true, associated ) );
				} );
			} );
		} );
	} );
};
<commit_msg>Fix update blueprint<commit_after>/**
 * Module dependencies
 */

var actionUtil = require( './_util/actionUtil' );
var util = require( 'util' );
var _ = require( 'lodash' );

/**
 * Update One Record
 *
 * An API call to update a model instance with the specified `id`,
 * treating the other unbound parameters as attributes.
 *
 * @param {Integer|String} id  - the unique id of the particular record you'd like to update  (Note: this param should be specified even if primary key is not `id`!!)
 * @param *                    - values to set on the record
 *
 */
module.exports = function updateOneRecord( req, res ) {

	// Look up the model
	var Model = actionUtil.parseModel( req );

	// Locate and validate the required `id` parameter.
	var pk = actionUtil.requirePk( req );

	// Create `values` object (monolithic combination of all parameters)
	// But omit the blacklisted params (like JSONP callback param, etc.)
	var values = actionUtil.parseValues( req, Model );

	// Omit the path parameter `id` from values, unless it was explicitly defined
	// elsewhere (body/query):
	// var idParamExplicitlyIncluded = ( ( req.body && req.body.id ) || req.query.id );
	// if ( !idParamExplicitlyIncluded ) delete values.id;

	// Look up the association configuration and determine how to populate the query
	// @todo support request driven selection of includes/populate
	var associations = actionUtil.getAssociationConfiguration( Model, "detail" );

	// Find and update the targeted record.
	//
	// (Note: this could be achieved in a single query, but a separate `findOne`
	//  is used first to provide a better experience for front-end developers
	//  integrating with the blueprint API.)
	Model.findOne( pk ).exec( function found( err, matchingRecord ) {
		if ( err ) return res.serverError( err );
		if ( !matchingRecord ) return res.notFound();

		Model.update( pk, values ).exec( function updated( err, records ) {

			// Differentiate between waterline-originated validation errors
			// and serious underlying issues. Respond with badRequest if a
			// validation error is encountered, w/ validation info.
			if ( err ) return res.negotiate( err );

			// Because this should only update a single record and update
			// returns an array, just use the first item.  If more than one
			// record was returned, something is amiss.
			if ( !records || !records.length || records.length > 1 ) {
				req._sails.log.warn(
					util.format( 'Unexpected output from `%s.update`.', Model.globalId )
				);
			}

			var updatedRecord = records[ 0 ];

			// If we have the pubsub hook, use the Model's publish method
			// to notify all subscribers about the update.
			if ( req._sails.hooks.pubsub ) {
				if ( req.isSocket ) {
					Model.subscribe( req, records );
				}
				Model.publishUpdate( pk, _.cloneDeep( values ), !req.options.mirror && req, {
					previous: matchingRecord.toJSON()
				} );
			}

			// Do a final query to populate the associations of the record.
			var Q = Model.findOne( updatedRecord[ Model.primaryKey ] );
			Q = actionUtil.populateRecords( Q, associations );
			Q.exec( function foundAgain( err, populatedRecord ) {
				if ( err ) return res.serverError( err );
				if ( !populatedRecord ) return res.serverError( 'Could not find record after updating!' );

				actionUtil.populateIndexes( Model, updatedRecord[ Model.primaryKey ], associations, function ( err, associated ) {

					res.ok( Ember.buildResponse( Model, populatedRecord, associations, true, associated ) );
				} );
			} );
		} );
	} );
};
<|endoftext|>


<commit_before>var dgram = require('dgram')
var net = require('net')

/**
 * Provides data from clients and servers going through the relay
 * @callback relayCallback
 * @param {Buffer} data
 * @param {Object} telemetry
 * @param {Object} telemetry.src
 * @param {string} telemetry.src.address
 * @param {number} telemetry.src.port
 * @param {Object} telemetry.dst
 * @param {string} telemetry.dst.address
 * @param {number} telemetry.dst.port
 */

/**
 * Create a UDP relay for Fallout 4's pip boy server
 * @param {Object} upstreamInfo
 * @param {string} upstreamInfo.address
 * @param {number} upstreamInfo.port
 * @param {relayCallback} - callback that handles new data
 */

var UDPRelay = function UDPRelay () {
  this.server = dgram.createSocket('udp4')
}

UDPRelay.prototype.bind = function UDPRelay (upstreamInfo, cb) {
  var server = this.server
  this.server.on('message', function (message, clientInfo) {
    var fakeClient = dgram.createSocket('udp4')

    fakeClient.on('message', function (message, serverInfo) {
      // Every time the fake client gets a message, we expect it from the Fallout server
      if (serverInfo.address === upstreamInfo.address &&
          serverInfo.port === upstreamInfo.port) {
        var copiedBuffer = new Buffer(message.length)
        message.copy(copiedBuffer)

        // Now emulate our server
        server.send(message, 0, message.length, clientInfo.port, clientInfo.address)
        var telemetry = {
          'src': serverInfo,
          'dst': clientInfo
        }
        cb(copiedBuffer, telemetry)
      } else {
        // TODO: Propagate an error
      }
    })

    // As soon as our client is ready, go ahead and send their message onward
    fakeClient.bind(undefined, undefined, function () {
      var copiedBuffer = new Buffer(message.length)
      message.copy(copiedBuffer)

      fakeClient.send(message, 0, message.length, upstreamInfo.port, upstreamInfo.address)
      var telemetry = {
        'src': clientInfo,
        'dst': upstreamInfo
      }
      cb(copiedBuffer, telemetry)
    })
  })

  this.server.bind(upstreamInfo.port, '0.0.0.0')
}

/**
 * Create a TCP relay for Fallout 4's pip boy server
 * @param {Object} upstreamInfo
 * @param {string} upstreamInfo.address
 * @param {number} upstreamInfo.port
 * @param {relayCallback} - callback that handles new data
 */
var TCPRelay = function TCPRelay () {
  this.server = net.createServer({'allowHalfOpen': true})
}

TCPRelay.prototype.listen = function listen (upstreamInfo, cb) {
  this.server.on('connection', function (client) {
    // Now we create our fake client
    var fakeClient = new net.Socket()
    fakeClient.connect(upstreamInfo.port, upstreamInfo.address)

    var serverInfo = {}
    serverInfo.address = fakeClient.remoteAddress
    serverInfo.port = fakeClient.remotePort
    serverInfo.family = fakeClient.remoteFamily

    var actualClientInfo = {}
    actualClientInfo.address = client.remoteAddress
    actualClientInfo.port = client.remotePort
    actualClientInfo.family = client.remoteFamily

    fakeClient.on('connect', function () {
      // Once we're connected, we can get each message from the client
      client.on('data', function (message) {
        var copiedBuffer = new Buffer(message.length)
        message.copy(copiedBuffer)

        // To the server
        fakeClient.write(message)

        var telemetry = {
          'src': actualClientInfo,
          'dst': serverInfo
        }

        cb(copiedBuffer, telemetry)
      })
    })

    fakeClient.on('data', function (message) {
      var copiedBuffer = new Buffer(message.length)
      message.copy(copiedBuffer)

      var serverInfo = {}
      serverInfo.address = fakeClient.remoteAddress
      serverInfo.port = fakeClient.remotePort
      serverInfo.family = fakeClient.remoteFamily

      var telemetry = {
        'src': serverInfo,
        'dst': actualClientInfo
      }
      client.write(message)

      cb(copiedBuffer, telemetry)
    })

    fakeClient.on('close', function (hadError) {
      if (hadError) {
        console.log('closure error')
      }
      client.close()
    })

    fakeClient.on('end', function () {
      console.log('ending')
      client.end()
    })

    fakeClient.on('error', function (err) {
      console.error(err)
    })

    fakeClient.on('timeout', function () {
      console.log('timeout')
    })

    fakeClient.on('drain', function () {
      console.log('drain')
    })
  })

  this.server.on('error', function (err) {
    console.error(err)
  })

  this.server.on('listening', function () {
    console.log('listening')
  })

  this.server.listen({'port': upstreamInfo.port})
}

TCPRelay.prototype.close = function close (cb) {
  this.server.close(cb)
}

module.exports = {
  UDPRelay: UDPRelay,
  TCPRelay: TCPRelay
}
<commit_msg>Genericize the relays.<commit_after>var dgram = require('dgram')
var net = require('net')

/**
 * Provides data from clients and servers going through the relay
 * @callback relayCallback
 * @param {Buffer} data
 * @param {Object} telemetry
 * @param {Object} telemetry.src
 * @param {string} telemetry.src.address
 * @param {number} telemetry.src.port
 * @param {Object} telemetry.dst
 * @param {string} telemetry.dst.address
 * @param {number} telemetry.dst.port
 */

/**
 * Create a UDP Relay
 * @constructor
 */
var UDPRelay = function UDPRelay () {
  this.server = dgram.createSocket('udp4')
}

/**
 * Bind the UDP Relay using upstreamInfo
 * @param {Object} upstreamInfo
 * @param {string} upstreamInfo.address
 * @param {number} upstreamInfo.port
 * @param {relayCallback} - callback that receives data
 */
UDPRelay.prototype.bind = function UDPRelay (upstreamInfo, cb) {
  var server = this.server
  this.server.on('message', function (message, clientInfo) {
    var fakeClient = dgram.createSocket('udp4')

    fakeClient.on('message', function (message, serverInfo) {
      // Every time the fake client gets a message, we expect it from the upstream server
      if (serverInfo.address === upstreamInfo.address &&
          serverInfo.port === upstreamInfo.port) {
        var copiedBuffer = new Buffer(message.length)
        message.copy(copiedBuffer)

        // Now emulate our server
        server.send(message, 0, message.length, clientInfo.port, clientInfo.address)
        var telemetry = {
          'src': serverInfo,
          'dst': clientInfo
        }
        cb(copiedBuffer, telemetry)
      } else {
        // Ignore responses not coming from the upstream server
      }
    })

    // As soon as our client is ready, go ahead and send their message onward
    fakeClient.bind(undefined, undefined, function () {
      var copiedBuffer = new Buffer(message.length)
      message.copy(copiedBuffer)

      fakeClient.send(message, 0, message.length, upstreamInfo.port, upstreamInfo.address)
      var telemetry = {
        'src': clientInfo,
        'dst': upstreamInfo
      }
      cb(copiedBuffer, telemetry)
    })
  })

  this.server.bind(upstreamInfo.port, '0.0.0.0')
}

/**
 * Create a TCP relay for an upstream server
 * @constructor
 */

/**
 * Listen for traffic to relay to/from an upstream server
 * @param {Object} upstreamInfo
 * @param {string} upstreamInfo.address
 * @param {number} upstreamInfo.port
 * @param {relayCallback} - callback that handles new data
 */
var TCPRelay = function TCPRelay () {
  this.server = net.createServer({'allowHalfOpen': true})
}

TCPRelay.prototype.listen = function listen (upstreamInfo, cb) {
  this.server.on('connection', function (client) {
    // Now we create our fake client
    var fakeClient = new net.Socket()
    fakeClient.connect(upstreamInfo.port, upstreamInfo.address)

    var serverInfo = {}
    serverInfo.address = fakeClient.remoteAddress
    serverInfo.port = fakeClient.remotePort
    serverInfo.family = fakeClient.remoteFamily

    var actualClientInfo = {}
    actualClientInfo.address = client.remoteAddress
    actualClientInfo.port = client.remotePort
    actualClientInfo.family = client.remoteFamily

    fakeClient.on('connect', function () {
      // Once we're connected, we can get each message from the client
      client.on('data', function (message) {
        var copiedBuffer = new Buffer(message.length)
        message.copy(copiedBuffer)

        // To the server
        fakeClient.write(message)

        var telemetry = {
          'src': actualClientInfo,
          'dst': serverInfo
        }

        cb(copiedBuffer, telemetry)
      })
    })

    fakeClient.on('data', function (message) {
      var copiedBuffer = new Buffer(message.length)
      message.copy(copiedBuffer)

      var serverInfo = {}
      serverInfo.address = fakeClient.remoteAddress
      serverInfo.port = fakeClient.remotePort
      serverInfo.family = fakeClient.remoteFamily

      var telemetry = {
        'src': serverInfo,
        'dst': actualClientInfo
      }
      client.write(message)

      cb(copiedBuffer, telemetry)
    })

    fakeClient.on('close', function (hadError) {
      if (hadError) {
        console.log('closure error')
      }
      client.close()
    })

    fakeClient.on('end', function () {
      console.log('ending')
      client.end()
    })

    fakeClient.on('error', function (err) {
      console.error(err)
    })

    fakeClient.on('timeout', function () {
      console.log('timeout')
    })

    fakeClient.on('drain', function () {
      console.log('drain')
    })
  })

  this.server.on('error', function (err) {
    console.error(err)
  })

  this.server.on('listening', function () {
    console.log('listening')
  })

  this.server.listen({'port': upstreamInfo.port})
}

TCPRelay.prototype.close = function close (cb) {
  this.server.close(cb)
}

module.exports = {
  UDPRelay: UDPRelay,
  TCPRelay: TCPRelay
}
<|endoftext|>


<commit_before>import React from 'react';
import {mount} from 'enzyme';
import {Provider} from 'react-redux';
import ConnectedNotificationsSystem, {
  NotificationsSystem,
  defaultValues
} from '../../src/components/NotificationsSystem';
import Notification from '../../src/components/Notification';
import NotificationsContainer from '../../src/components/NotificationsContainer';
import {genNotifications, mockStore} from '../utils/fixtures';
import {ExpectedNotificationsSystem} from '../utils/expectedComponents';
import {STATUS, POSITIONS} from '../../src/constants';
import theme from '../../src/themes/wybo';

describe('<NotificationsSystem/>', () => {
  const otherProps = {
    theme
  };

  it('should mount with default props', () => {
    const props = mount(<NotificationsSystem {...otherProps}/>).props();
    expect(props.notifications).toEqual([]);
    expect(props.defaultValues).toEqual(defaultValues);
  });

  it('should mount with custom props', () => {
    const customDefaultValue = {
      status: STATUS.success,
      position: POSITIONS.bottomRight,
      dismissible: false,
      dismissAfter: 99999,
      allowHTML: true
    };
    const props = mount(<NotificationsSystem
      defaultValues={customDefaultValue} {...otherProps}/>).props();
    expect(props.defaultValues).toEqual(customDefaultValue);
    expect(props.defaultValues).toNotEqual(defaultValues);
  });

  it('should render 1 empty notifications container (mobile)', () => {
    const wrapper = mount(<NotificationsSystem {...otherProps}/>);
    const expectedWrapper = mount(<ExpectedNotificationsSystem {...otherProps}/>);
    expect(wrapper.find(NotificationsContainer).length).toEqual(1);
    expect(wrapper.find(Notification).length).toEqual(0);
    expect(wrapper.html()).toEqual(expectedWrapper.html());
  });

  it('should render 4 empty notifications containers (desktop)', () => {
    // define the small screen breakpoint under default window width of PhantomJS (400px)
    // to simulate a desktop screen and test a portion of code
    const customTheme = Object.assign({}, theme, {smallScreenMin: 380});
    const wrapper = mount(<NotificationsSystem theme={customTheme}/>);
    const expectedWrapper = mount(<ExpectedNotificationsSystem theme={customTheme}/>);
    expect(wrapper.find(NotificationsContainer).length).toEqual(4);
    expect(wrapper.find(Notification).length).toEqual(0);
    expect(wrapper.html()).toEqual(expectedWrapper.html());
  });

  it('should render notifications in 1 notifications containers (mobile)', () => {
    const notifications = genNotifications(3);
    const store = mockStore({notifications});
    const wrapper = mount(
      <Provider store={store}>
        <ConnectedNotificationsSystem notifications={notifications} {...otherProps}/>
      </Provider>
    );
    const expectedWrapper = mount(
      <Provider store={store}>
        <ExpectedNotificationsSystem notifications={notifications} {...otherProps}/>
      </Provider>
    );
    expect(wrapper.find(NotificationsContainer).length).toEqual(1);
    expect(wrapper.find(Notification).length).toEqual(3);
    expect(wrapper.html()).toEqual(expectedWrapper.html());
  });

  it('should render notifications in 4 notifications containers (desktop)', () => {
    const notifications = genNotifications(3);
    const store = mockStore({notifications});
    // define the small screen breakpoint under default window width of PhantomJS (400px)
    // to simulate a desktop screen and test a portion of code
    const customTheme = Object.assign({}, theme, {smallScreenMin: 380});
    const wrapper = mount(
      <Provider store={store}>
        <ConnectedNotificationsSystem notifications={notifications} theme={customTheme}/>
      </Provider>
    );
    const expectedWrapper = mount(
      <Provider store={store}>
        <ExpectedNotificationsSystem notifications={notifications} theme={customTheme}/>
      </Provider>
    );
    expect(wrapper.find(NotificationsContainer).length).toEqual(4);
    expect(wrapper.find(Notification).length).toEqual(3);
    expect(wrapper.html()).toEqual(expectedWrapper.html());
  });
});
<commit_msg>add test for propTypes of NotificationsSystem component<commit_after>import React from 'react';
import {mount} from 'enzyme';
import {Provider} from 'react-redux';
import ConnectedNotificationsSystem, {
  NotificationsSystem,
  defaultValues
} from '../../src/components/NotificationsSystem';
import Notification from '../../src/components/Notification';
import NotificationsContainer from '../../src/components/NotificationsContainer';
import {genNotifications, mockStore, checkPropTypes} from '../utils/fixtures';
import {ExpectedNotificationsSystem} from '../utils/expectedComponents';
import {STATUS, POSITIONS} from '../../src/constants';
import theme from '../../src/themes/wybo';

describe('<NotificationsSystem/>', () => {
  const otherProps = {
    theme
  };

  it('should not throw error during propTypes validation', () => {
    checkPropTypes({
      notifications: [],
      theme,
      defaultValues
    }, NotificationsSystem.propTypes, true)
  });

  it('should mount with default props', () => {
    const props = mount(<NotificationsSystem {...otherProps}/>).props();
    expect(props.notifications).toEqual([]);
    expect(props.defaultValues).toEqual(defaultValues);
  });

  it('should mount with custom props', () => {
    const customDefaultValue = {
      status: STATUS.success,
      position: POSITIONS.bottomRight,
      dismissible: false,
      dismissAfter: 99999,
      allowHTML: true
    };
    const props = mount(<NotificationsSystem
      defaultValues={customDefaultValue} {...otherProps}/>).props();
    expect(props.defaultValues).toEqual(customDefaultValue);
    expect(props.defaultValues).toNotEqual(defaultValues);
  });

  it('should render 1 empty notifications container (mobile)', () => {
    const wrapper = mount(<NotificationsSystem {...otherProps}/>);
    const expectedWrapper = mount(<ExpectedNotificationsSystem {...otherProps}/>);
    expect(wrapper.find(NotificationsContainer).length).toEqual(1);
    expect(wrapper.find(Notification).length).toEqual(0);
    expect(wrapper.html()).toEqual(expectedWrapper.html());
  });

  it('should render 4 empty notifications containers (desktop)', () => {
    // define the small screen breakpoint under default window width of PhantomJS (400px)
    // to simulate a desktop screen and test a portion of code
    const customTheme = Object.assign({}, theme, {smallScreenMin: 380});
    const wrapper = mount(<NotificationsSystem theme={customTheme}/>);
    const expectedWrapper = mount(<ExpectedNotificationsSystem theme={customTheme}/>);
    expect(wrapper.find(NotificationsContainer).length).toEqual(4);
    expect(wrapper.find(Notification).length).toEqual(0);
    expect(wrapper.html()).toEqual(expectedWrapper.html());
  });

  it('should render notifications in 1 notifications containers (mobile)', () => {
    const notifications = genNotifications(3);
    const store = mockStore({notifications});
    const wrapper = mount(
      <Provider store={store}>
        <ConnectedNotificationsSystem notifications={notifications} {...otherProps}/>
      </Provider>
    );
    const expectedWrapper = mount(
      <Provider store={store}>
        <ExpectedNotificationsSystem notifications={notifications} {...otherProps}/>
      </Provider>
    );
    expect(wrapper.find(NotificationsContainer).length).toEqual(1);
    expect(wrapper.find(Notification).length).toEqual(3);
    expect(wrapper.html()).toEqual(expectedWrapper.html());
  });

  it('should render notifications in 4 notifications containers (desktop)', () => {
    const notifications = genNotifications(3);
    const store = mockStore({notifications});
    // define the small screen breakpoint under default window width of PhantomJS (400px)
    // to simulate a desktop screen and test a portion of code
    const customTheme = Object.assign({}, theme, {smallScreenMin: 380});
    const wrapper = mount(
      <Provider store={store}>
        <ConnectedNotificationsSystem notifications={notifications} theme={customTheme}/>
      </Provider>
    );
    const expectedWrapper = mount(
      <Provider store={store}>
        <ExpectedNotificationsSystem notifications={notifications} theme={customTheme}/>
      </Provider>
    );
    expect(wrapper.find(NotificationsContainer).length).toEqual(4);
    expect(wrapper.find(Notification).length).toEqual(3);
    expect(wrapper.html()).toEqual(expectedWrapper.html());
  });
});
<|endoftext|>


<commit_before>const fs = require('fs');
const path = require('path');
const fileUrl = require('file-url');
const failFast = require('protractor-fail-fast');

const failFastActive = (process.argv.length > 4 && process.argv[4] == '--params=ff');

exports.config = {

    seleniumAddress: 'http://localhost:9515/wd/hub',
    baseUrl: fileUrl(path.resolve(__dirname, '../../..') + '/index.html'),

    suites: {
        default: [
            '../settings/*.spec.js',
            '../syncing/*.spec.js',
            '../images/*.spec.js',
            '../list/*.spec.js',
            '../resources/*.spec.js',
            '../map/*.spec.js',
            '../widgets/*.spec.js',
            '../import/*.spec.js'
        ],
        flaky: [
            '../flaky/*.spec.js'
        ]
    },

    allScriptsTimeout: 110000,
    getPageTimeout: 100000,
    framework: 'jasmine2',
    jasmineNodeOpts: {
        isVerbose: false,
        showColors: true,
        includeStackTrace: false,
        defaultTimeoutInterval: 400000
    },
    plugins: [{
        package: 'protractor-console-plugin',
        failOnWarning: true,
        failOnError: true,
        logWarnings: true,
        exclude: [
            "http://localhost:3001/" // pouchdb issues ignorable errors when syncing
        ]
    }],
    params: {
        appDataPath: 'test/test-temp',
        configPath: 'config/config.test.json',
        configTemplate: { 'dbs' : ['test'] }
    },
    onPrepare: function() {
        if (failFastActive) jasmine.getEnv().addReporter(failFast.init());

        var ProgressReporter = function() {

            this.specStarted = function(spec) {
                process.stdout.write("SPEC " + spec.fullName + " ")
            };

            this.specDone = function(spec) {
                console.log(spec.status.toUpperCase());

                browser.manage().logs().get('browser').then(function(browserLogs) {

                    var errLogs = browserLogs.filter(function (log) {
                        return (log.level.value_ > 900 && (log.message.
                            indexOf('Failed to load resource') === -1))});

                    if (errLogs.length > 0) {

                        console.log('--- There has been at least one browser console error (Spec'+spec.fullName+')!');

                        browserLogs.forEach(function(log){
                            if (log.level.value_ > 900) { // it's an error log
                                if (log.message.
                                    indexOf('Failed to load resource') === -1) {

                                    console.log("===> critical ERROR message: ",log.message);
                                } else {
                                    console.log("(uncritical) Error message: ",log.message);
                                }
                            } else {
                                // console.log("Log message: ",log.message);
                            }
                        });
                    }

                });

            }
        };
        jasmine.getEnv().addReporter(new ProgressReporter());
    },
    afterLaunch: function() {
        if (failFastActive) failFast.clean();
    },
    /**
     * ng2 related configuration
     *
     * useAllAngular2AppRoots: tells Protractor to wait for any angular2 apps on the page instead of just the one matching
     * `rootEl`
     *
     */
    useAllAngular2AppRoots: true
};

if (failFastActive) exports.config.plugins.push({package: 'protractor-fail-fast'});<commit_msg>show msg with context and in correct order<commit_after>const fs = require('fs');
const path = require('path');
const fileUrl = require('file-url');
const failFast = require('protractor-fail-fast');

const failFastActive = (process.argv.length > 4 && process.argv[4] == '--params=ff');

exports.config = {

    seleniumAddress: 'http://localhost:9515/wd/hub',
    baseUrl: fileUrl(path.resolve(__dirname, '../../..') + '/index.html'),

    suites: {
        default: [
            '../settings/*.spec.js',
            '../syncing/*.spec.js',
            '../images/*.spec.js',
            '../list/*.spec.js',
            '../resources/*.spec.js',
            '../map/*.spec.js',
            '../widgets/*.spec.js',
            '../import/*.spec.js'
        ],
        flaky: [
            '../flaky/*.spec.js'
        ]
    },

    allScriptsTimeout: 110000,
    getPageTimeout: 100000,
    framework: 'jasmine2',
    jasmineNodeOpts: {
        isVerbose: false,
        showColors: true,
        includeStackTrace: false,
        defaultTimeoutInterval: 400000
    },
    plugins: [{
        package: 'protractor-console-plugin',
        failOnWarning: true,
        failOnError: true,
        logWarnings: true,
        exclude: [
            "http://localhost:3001/" // pouchdb issues ignorable errors when syncing
        ]
    }],
    params: {
        appDataPath: 'test/test-temp',
        configPath: 'config/config.test.json',
        configTemplate: { 'dbs' : ['test'] }
    },
    onPrepare: function() {
        if (failFastActive) jasmine.getEnv().addReporter(failFast.init());

        var ProgressReporter = function() {

            this.specStarted = function(spec) {
                setTimeout(function() {
                    process.stdout.write("SPEC " + spec.fullName + " ")
                },20)

            };

            this.specDone = function(spec) {
                console.log(spec.status.toUpperCase());

                browser.manage().logs().get('browser').then(function(browserLogs) {

                    var errLogs = browserLogs.filter(function (log) {
                        return (log.level.value_ > 900 && (log.message.
                            indexOf('Failed to load resource') === -1))});

                    if (errLogs.length > 0) {

                        console.log('--- There has been at least one browser console error (Spec '+spec.fullName+')!');

                        browserLogs.forEach(function(log){
                            if (log.level.value_ > 900) { // it's an error log
                                if (log.message.
                                    indexOf('Failed to load resource') === -1) {

                                    console.log("===> critical ERROR message: ",log.message);
                                } else {
                                    console.log("(uncritical) Error message: ",log.message);
                                }
                            } else {
                                console.log("Log message: ",log.message);
                            }
                        });
                    }

                });

            }
        };
        jasmine.getEnv().addReporter(new ProgressReporter());
    },
    afterLaunch: function() {
        if (failFastActive) failFast.clean();
    },
    /**
     * ng2 related configuration
     *
     * useAllAngular2AppRoots: tells Protractor to wait for any angular2 apps on the page instead of just the one matching
     * `rootEl`
     *
     */
    useAllAngular2AppRoots: true
};

if (failFastActive) exports.config.plugins.push({package: 'protractor-fail-fast'});<|endoftext|>


<commit_before>const fs = require('fs');
const path = require('path');
const fileUrl = require('file-url');
const failFast = require('protractor-fail-fast');

const failFastActive = (process.argv.length > 4 && process.argv[4] == '--params=ff');

exports.config = {

    seleniumAddress: 'http://localhost:9515/wd/hub',
    baseUrl: fileUrl(path.resolve(__dirname, '../../..') + '/index.html'),

    specs: [
        '../matrix/*.spec.js',
        '../syncing/*.spec.js',
        '../project/*.spec.js',
        '../resources/resources.spec.js',
        '../resources/resources-state.spec.js',
        '../images/*.spec.js',
        '../map/*.spec.js',
        '../import/*.spec.js',
        '../settings/*.spec.js'
    ],

    allScriptsTimeout: 110000,
    getPageTimeout: 100000,
    framework: 'jasmine2',
    jasmineNodeOpts: {
        isVerbose: false,
        showColors: true,
        includeStackTrace: false,
        defaultTimeoutInterval: 400000
    },
    plugins: [{
        package: 'protractor-console-plugin',
        failOnWarning: true,
        failOnError: true,
        logWarnings: true,
        exclude: [
            "http://localhost:3001/" // pouchdb issues ignorable errors when syncing
        ]
    }],
    params: {
        appDataPath: 'test/test-temp',
        configPath: 'config/config.test.json',
        configTemplate: { 'dbs' : ['test'] }
    },
    onPrepare: function() {
        if (failFastActive) jasmine.getEnv().addReporter(failFast.init());

        var ProgressReporter = function() {

            this.specStarted = function(spec) {
                process.stdout.write("SPEC " + spec.fullName + " ")
            };

            this.specDone = function(spec) {

                console.log(spec.status.toUpperCase());

                browser.manage().logs().get('browser').then(function(browserLogs) {

                    var errLogs = browserLogs.filter(function (log) {
                        return (log.level.value_ > 900 && (log.message.
                            indexOf('Failed to load resource') === -1))});

                    if (errLogs.length > 0) {

                        console.log('--- There has been at least one browser console error (Spec '+spec.fullName+')!');

                        browserLogs.forEach(function(log){
                            if (log.level.value_ > 900) { // it's an error log
                                if (log.message.
                                    indexOf('Failed to load resource') === -1) {

                                    console.log("===> critical ERROR message: ",log.message);
                                } else {
                                    console.log("(uncritical) Error message: ",log.message);
                                }
                            } else {
                                console.log("Log message: ",log.message);
                            }
                        });
                    }

                });

            }
        };
        jasmine.getEnv().addReporter(new ProgressReporter());
    },
    afterLaunch: function() {
        if (failFastActive) failFast.clean();
    },
    /**
     * ng2 related configuration
     *
     * useAllAngular2AppRoots: tells Protractor to wait for any angular2 apps on the page instead of just the one matching
     * `rootEl`
     *
     */
    useAllAngular2AppRoots: true
};

if (failFastActive) exports.config.plugins.push({package: 'protractor-fail-fast'});<commit_msg>change order of e2e tests<commit_after>const fs = require('fs');
const path = require('path');
const fileUrl = require('file-url');
const failFast = require('protractor-fail-fast');

const failFastActive = (process.argv.length > 4 && process.argv[4] == '--params=ff');

exports.config = {

    seleniumAddress: 'http://localhost:9515/wd/hub',
    baseUrl: fileUrl(path.resolve(__dirname, '../../..') + '/index.html'),

    specs: [
        '../syncing/*.spec.js',
        '../project/*.spec.js',
        '../resources/resources.spec.js',
        '../resources/resources-state.spec.js',
        '../images/*.spec.js',
        '../map/*.spec.js',
        '../matrix/*.spec.js',
        '../import/*.spec.js',
        '../settings/*.spec.js'
    ],

    allScriptsTimeout: 110000,
    getPageTimeout: 100000,
    framework: 'jasmine2',
    jasmineNodeOpts: {
        isVerbose: false,
        showColors: true,
        includeStackTrace: false,
        defaultTimeoutInterval: 400000
    },
    plugins: [{
        package: 'protractor-console-plugin',
        failOnWarning: true,
        failOnError: true,
        logWarnings: true,
        exclude: [
            "http://localhost:3001/" // pouchdb issues ignorable errors when syncing
        ]
    }],
    params: {
        appDataPath: 'test/test-temp',
        configPath: 'config/config.test.json',
        configTemplate: { 'dbs' : ['test'] }
    },
    onPrepare: function() {
        if (failFastActive) jasmine.getEnv().addReporter(failFast.init());

        var ProgressReporter = function() {

            this.specStarted = function(spec) {
                process.stdout.write("SPEC " + spec.fullName + " ")
            };

            this.specDone = function(spec) {

                console.log(spec.status.toUpperCase());

                browser.manage().logs().get('browser').then(function(browserLogs) {

                    var errLogs = browserLogs.filter(function (log) {
                        return (log.level.value_ > 900 && (log.message.
                            indexOf('Failed to load resource') === -1))});

                    if (errLogs.length > 0) {

                        console.log('--- There has been at least one browser console error (Spec '+spec.fullName+')!');

                        browserLogs.forEach(function(log){
                            if (log.level.value_ > 900) { // it's an error log
                                if (log.message.
                                    indexOf('Failed to load resource') === -1) {

                                    console.log("===> critical ERROR message: ",log.message);
                                } else {
                                    console.log("(uncritical) Error message: ",log.message);
                                }
                            } else {
                                console.log("Log message: ",log.message);
                            }
                        });
                    }

                });

            }
        };
        jasmine.getEnv().addReporter(new ProgressReporter());
    },
    afterLaunch: function() {
        if (failFastActive) failFast.clean();
    },
    /**
     * ng2 related configuration
     *
     * useAllAngular2AppRoots: tells Protractor to wait for any angular2 apps on the page instead of just the one matching
     * `rootEl`
     *
     */
    useAllAngular2AppRoots: true
};

if (failFastActive) exports.config.plugins.push({package: 'protractor-fail-fast'});<|endoftext|>


<commit_before>module("Integration: Header", {
  setup: function() {
    sinon.stub(I18n, "t", function(scope, options) {
      if (options) {
        return [scope, options.username, options.link].join(" ").trim();
      }
      return scope;
    });
    Discourse.reset();
  },

  teardown: function() {
    I18n.t.restore();
  }
});

test("header", function() {
  expect(1);

  visit("/").then(function() {
    ok(exists("header"), "is rendered");
  });
});

test("logo", function() {
  expect(2);

  visit("/").then(function() {
    ok(exists(".logo-big"), "is rendered");

    Ember.run(function() {
      controllerFor("header").set("showExtraInfo", true);
    });
    ok(exists(".logo-small"), "is properly wired to showExtraInfo property (when showExtraInfo value changes, logo size also changes)");
  });
});

var notificationsDropdown = function() {
  return Ember.$("#notifications-dropdown");
};

var notificationFixture = [
  {
    notification_type: 1, //mentioned
    read: false,
    created_at: "2013-11-03T12:12:12-04:00",
    post_number: 1, //post number == 1 means no number included in URL
    topic_id: 1234,
    slug: "some-topic-title",
    data: {
      topic_title: "Some topic title",
      display_username: "velesin"
    }
  },
  {
    notification_type: 2, //replied
    read: true,
    created_at: "2013-11-02T10:10:10-04:00",
    post_number: 2, //post number > 1 means number is included in URL
    topic_id: 1234,
    slug: "", //no slug == differently formatted URL (hardcoded 'topic/' segment instead of slug)
    data: {
      topic_title: "Some topic title",
      display_username: "velesin"
    }
  },
  {
    notification_type: 5, //liked
    read: true,
    created_at: "2013-11-01T11:11:11-04:00",
    post_number: 2,
    topic_id: 1234,
    slug: "some-topic-title",
    data: {
      topic_title: "", //no title == link URL should be empty
      display_username: "velesin"
    }
  }
];

test("notifications: flow", function() {
  expect(8);

  Ember.run(function() {
    Discourse.URL_FIXTURES["/notifications"] = [notificationFixture[0]];
    Discourse.User.current().set("unread_notifications", 1);
  });

  visit("/")
    .then(function() {
      equal(notificationsDropdown().find("ul").length, 0, "initially a list of notifications is not loaded");
      equal(notificationsDropdown().find("div.none").length, 1, "initially special 'no notifications' message is displayed");
      equal(notificationsDropdown().find("div.none").text(), "notifications.none", "'no notifications' message contains proper internationalized text");
      equal(Discourse.User.current().get("unread_notifications"), 1, "initially current user's unread notification count is not reset");
    })
    .click("#user-notifications")
    .then(function() {
      equal(notificationsDropdown().find("li").length, 2, "after user opens notifications dropdown, notifications are loaded");
      equal(Discourse.User.current().get("unread_notifications"), 0, "after user opens notifications dropdown, current user's notification count is zeroed");
    })
    .then(function() {
      Ember.run(function() {
        Discourse.URL_FIXTURES["/notifications"] = [notificationFixture[0], notificationFixture[1]];
        Discourse.User.current().set("unread_notifications", 1);
      });
    })
    .click("#user-notifications")
    .then(function() {
      equal(notificationsDropdown().find("li").length, 3, "when user opens notifications dropdown for the second time, notifications are reloaded afresh");
      equal(Discourse.User.current().get("unread_notifications"), 0, "when user opens notifications dropdown for the second time, current user's notification count is zeroed again");
    });
});

test("notifications: when there are no notifications", function() {
  expect(3);

  Discourse.URL_FIXTURES["/notifications"] = [];

  visit("/")
  .click("#user-notifications")
  .then(function() {
    equal(notificationsDropdown().find("ul").length, 0, "a list of notifications is not displayed");
    equal(notificationsDropdown().find("div.none").length, 1, "special 'no notifications' message is displayed");
    equal(notificationsDropdown().find("div.none").text(), "notifications.none", "'no notifications' message contains proper internationalized text");
  });
});

test("notifications: content", function() {
  expect(9);

  Ember.run(function() {
    Discourse.URL_FIXTURES["/notifications"] = notificationFixture;
    Discourse.User.current().set("unread_notifications", 2);
  });

  visit("/")
    .click("#user-notifications")
    .then(function() {
      equal(notificationsDropdown().find("li").length, 4, "dropdown contains list items for all notifications plus for additional 'more' link");

      equal(notificationsDropdown().find("li").eq(0).attr("class"), "", "list item for unread notification has no class");
      equal(notificationsDropdown().find("li").eq(0).html(), 'notifications.mentioned velesin Some topic title', "notification with a slug and for the first post in a topic is rendered correctly");

      equal(notificationsDropdown().find("li").eq(1).attr("class"), "read", "list item for read notification has correct class");
      equal(notificationsDropdown().find("li").eq(1).html(), 'notifications.replied velesin Some topic title', "notification without a slug and for a non-first post in a topic is rendered correctly");

      equal(notificationsDropdown().find("li").eq(2).html(), 'notifications.liked velesin ', "notification without topic title is rendered correctly");

      equal(notificationsDropdown().find("li").eq(3).attr("class"), "read last", "list item for 'more' link has correct class");
      equal(notificationsDropdown().find("li").eq(3).find("a").attr("href"), Discourse.User.current().get("path"), "'more' link points to a correct URL");
      equal(notificationsDropdown().find("li").eq(3).find("a").text(), "notifications.more" + " …", "'more' link has correct text");
    });
});
<commit_msg>cleans up setup of header integration test<commit_after>integration("Header", {
  setup: function() {
    sinon.stub(I18n, "t", function(scope, options) {
      if (options) {
        return [scope, options.username, options.link].join(" ").trim();
      }
      return scope;
    });
    Discourse.reset();
  },

  teardown: function() {
    I18n.t.restore();
  }
});

test("header", function() {
  expect(1);

  visit("/").then(function() {
    ok(exists("header"), "is rendered");
  });
});

test("logo", function() {
  expect(2);

  visit("/").then(function() {
    ok(exists(".logo-big"), "is rendered");

    Ember.run(function() {
      controllerFor("header").set("showExtraInfo", true);
    });
    ok(exists(".logo-small"), "is properly wired to showExtraInfo property (when showExtraInfo value changes, logo size also changes)");
  });
});

var notificationsDropdown = function() {
  return Ember.$("#notifications-dropdown");
};

var notificationFixture = [
  {
    notification_type: 1, //mentioned
    read: false,
    created_at: "2013-11-03T12:12:12-04:00",
    post_number: 1, //post number == 1 means no number included in URL
    topic_id: 1234,
    slug: "some-topic-title",
    data: {
      topic_title: "Some topic title",
      display_username: "velesin"
    }
  },
  {
    notification_type: 2, //replied
    read: true,
    created_at: "2013-11-02T10:10:10-04:00",
    post_number: 2, //post number > 1 means number is included in URL
    topic_id: 1234,
    slug: "", //no slug == differently formatted URL (hardcoded 'topic/' segment instead of slug)
    data: {
      topic_title: "Some topic title",
      display_username: "velesin"
    }
  },
  {
    notification_type: 5, //liked
    read: true,
    created_at: "2013-11-01T11:11:11-04:00",
    post_number: 2,
    topic_id: 1234,
    slug: "some-topic-title",
    data: {
      topic_title: "", //no title == link URL should be empty
      display_username: "velesin"
    }
  }
];

test("notifications: flow", function() {
  expect(8);

  Ember.run(function() {
    Discourse.URL_FIXTURES["/notifications"] = [notificationFixture[0]];
    Discourse.User.current().set("unread_notifications", 1);
  });

  visit("/")
    .then(function() {
      equal(notificationsDropdown().find("ul").length, 0, "initially a list of notifications is not loaded");
      equal(notificationsDropdown().find("div.none").length, 1, "initially special 'no notifications' message is displayed");
      equal(notificationsDropdown().find("div.none").text(), "notifications.none", "'no notifications' message contains proper internationalized text");
      equal(Discourse.User.current().get("unread_notifications"), 1, "initially current user's unread notification count is not reset");
    })
    .click("#user-notifications")
    .then(function() {
      equal(notificationsDropdown().find("li").length, 2, "after user opens notifications dropdown, notifications are loaded");
      equal(Discourse.User.current().get("unread_notifications"), 0, "after user opens notifications dropdown, current user's notification count is zeroed");
    })
    .then(function() {
      Ember.run(function() {
        Discourse.URL_FIXTURES["/notifications"] = [notificationFixture[0], notificationFixture[1]];
        Discourse.User.current().set("unread_notifications", 1);
      });
    })
    .click("#user-notifications")
    .then(function() {
      equal(notificationsDropdown().find("li").length, 3, "when user opens notifications dropdown for the second time, notifications are reloaded afresh");
      equal(Discourse.User.current().get("unread_notifications"), 0, "when user opens notifications dropdown for the second time, current user's notification count is zeroed again");
    });
});

test("notifications: when there are no notifications", function() {
  expect(3);

  Discourse.URL_FIXTURES["/notifications"] = [];

  visit("/")
  .click("#user-notifications")
  .then(function() {
    equal(notificationsDropdown().find("ul").length, 0, "a list of notifications is not displayed");
    equal(notificationsDropdown().find("div.none").length, 1, "special 'no notifications' message is displayed");
    equal(notificationsDropdown().find("div.none").text(), "notifications.none", "'no notifications' message contains proper internationalized text");
  });
});

test("notifications: content", function() {
  expect(9);

  Ember.run(function() {
    Discourse.URL_FIXTURES["/notifications"] = notificationFixture;
    Discourse.User.current().set("unread_notifications", 2);
  });

  visit("/")
    .click("#user-notifications")
    .then(function() {
      equal(notificationsDropdown().find("li").length, 4, "dropdown contains list items for all notifications plus for additional 'more' link");

      equal(notificationsDropdown().find("li").eq(0).attr("class"), "", "list item for unread notification has no class");
      equal(notificationsDropdown().find("li").eq(0).html(), 'notifications.mentioned velesin Some topic title', "notification with a slug and for the first post in a topic is rendered correctly");

      equal(notificationsDropdown().find("li").eq(1).attr("class"), "read", "list item for read notification has correct class");
      equal(notificationsDropdown().find("li").eq(1).html(), 'notifications.replied velesin Some topic title', "notification without a slug and for a non-first post in a topic is rendered correctly");

      equal(notificationsDropdown().find("li").eq(2).html(), 'notifications.liked velesin ', "notification without topic title is rendered correctly");

      equal(notificationsDropdown().find("li").eq(3).attr("class"), "read last", "list item for 'more' link has correct class");
      equal(notificationsDropdown().find("li").eq(3).find("a").attr("href"), Discourse.User.current().get("path"), "'more' link points to a correct URL");
      equal(notificationsDropdown().find("li").eq(3).find("a").text(), "notifications.more" + " …", "'more' link has correct text");
    });
});
<|endoftext|>


<commit_before>const frisby = require('frisby')
const Joi = frisby.Joi
const insecurity = require('../../lib/insecurity')
const config = require('config')

const christmasProduct = config.get('products').filter(product => product.useForChristmasSpecialChallenge)[0]

const tamperingProductId = ((() => {
  const products = config.get('products')
  for (let i = 0; i < products.length; i++) {
    if (products[i].useForProductTamperingChallenge) {
      return i + 1
    }
  }
})())

const API_URL = 'http://localhost:3000/api'
const REST_URL = 'http://localhost:3000/rest'

const authHeader = { 'Authorization': 'Bearer ' + insecurity.authorize(), 'content-type': 'application/json' }
const jsonHeader = { 'content-type': 'application/json' }

describe('/api/Products', () => {
  it('GET all products', done => {
    frisby.get(API_URL + '/Products')
      .expect('status', 200)
      .expect('header', 'content-type', /application\/json/)
      .expect('jsonTypes', 'data.*', {
        id: Joi.number(),
        name: Joi.string(),
        description: Joi.string(),
        price: Joi.number(),
        image: Joi.string()
      }).done(done)
  })

  it('POST new product is forbidden via public API', done => {
    frisby.post(API_URL + '/Products', {
      name: 'Dirt Juice (1000ml)',
      description: 'Made from ugly dirt.',
      price: 0.99,
      image: 'dirt_juice.jpg'
    })
      .expect('status', 401)
      .done(done)
  })

  it('POST new product does not filter XSS attacks', done => {
    frisby.post(API_URL + '/Products', {
      headers: authHeader,
      body: {
        name: 'XSS Juice (42ml)',
        description: '',
        price: 9999.99,
        image: 'xss3juice.jpg'
      }
    })
      .expect('header', 'content-type', /application\/json/)
      .expect('json', 'data', { description: '' })
      .done(done)
  })
})

describe('/api/Products/:id', () => {
  it('GET existing product by id', done => {
    frisby.get(API_URL + '/Products/1')
      .expect('status', 200)
      .expect('header', 'content-type', /application\/json/)
      .expect('jsonTypes', 'data', {
        id: Joi.number(),
        name: Joi.string(),
        description: Joi.string(),
        price: Joi.number(),
        image: Joi.string(),
        createdAt: Joi.string(),
        updatedAt: Joi.string()
      })
      .expect('json', 'data', { id: 1 })
      .done(done)
  })

  it('GET non-existing product by id', done => {
    frisby.get(API_URL + '/Products/4711')
      .expect('status', 404)
      .expect('header', 'content-type', /application\/json/)
      .expect('json', 'data', {})
      .done(done)
  })

  it('PUT update existing product is possible due to Missing Function-Level Access Control vulnerability', done => {
    frisby.put(API_URL + '/Products/' + tamperingProductId, {
      header: jsonHeader,
      body: {
        description: 'More...'
      }
    })
      .expect('status', 200)
      .expect('header', 'content-type', /application\/json/)
      .expect('json', 'data', { description: 'More...' })
      .done(done)
  })

  it('PUT update existing product does not filter XSS attacks', done => {
    frisby.put(API_URL + '/Products/1', {
      header: jsonHeader,
      body: {
        description: ""
      }
    })
      .expect('status', 200)
      .expect('header', 'content-type', /application\/json/)
      .expect('json', 'data', { description: "" })
      .done(done)
  })

  it('DELETE existing product is forbidden via public API', done => {
    frisby.del(API_URL + '/Products/1')
      .expect('status', 401)
      .done(done)
  })

  it('DELETE existing product is forbidden via API even when authenticated', done => {
    frisby.del(API_URL + '/Products/1', { headers: authHeader })
      .expect('status', 401)
      .done(done)
  })
})

describe('/rest/product/search', () => {
  it('GET product search with no matches returns no products', done => {
    frisby.get(REST_URL + '/product/search?q=nomatcheswhatsoever')
      .expect('status', 200)
      .expect('header', 'content-type', /application\/json/)
      .then(res => {
        expect(res.json.data.length).toBe(0)
      })
      .done(done)
  })

  it('GET product search with one match returns found product', done => {
    frisby.get(REST_URL + '/product/search?q=o-saft')
      .expect('status', 200)
      .expect('header', 'content-type', /application\/json/)
      .then(res => {
        expect(res.json.data.length).toBe(1)
      })
      .done(done)
  })

  it('GET product search with XSS attack is not blocked', done => {
    frisby.get(REST_URL + '/product/search?q=')
      .expect('status', 200)
      .expect('header', 'content-type', /application\/json/)
      .done(done)
  })

  it('GET product search fails with error message that exposes ins SQL Injection vulnerability', done => {
    frisby.get(REST_URL + '/product/search?q=\';')
      .expect('status', 500)
      .expect('header', 'content-type', /text\/html/)
      .expect('bodyContains', '
Juice Shop (Express ~')
      .expect('bodyContains', 'SQLITE_ERROR: near ";": syntax error')
      .done(done)
  })

  it('GET product search SQL Injection fails from two missing closing parenthesis', done => {
    frisby.get(REST_URL + '/product/search?q=\' union select null,id,email,password,null,null,null from users--')
      .expect('status', 500)
      .expect('header', 'content-type', /text\/html/)
      .expect('bodyContains', '
Juice Shop (Express ~')
      .expect('bodyContains', 'SQLITE_ERROR: near "union": syntax error')
      .done(done)
  })

  it('GET product search SQL Injection fails from one missing closing parenthesis', done => {
    frisby.get(REST_URL + '/product/search?q=\') union select null,id,email,password,null,null,null from users--')
      .expect('status', 500)
      .expect('header', 'content-type', /text\/html/)
      .expect('bodyContains', '
Juice Shop (Express ~')
      .expect('bodyContains', 'SQLITE_ERROR: near "union": syntax error')
      .done(done)
  })

  it('GET product search SQL Injection fails for SELECT * FROM attack due to wrong number of returned columns', done => {
    frisby.get(REST_URL + '/product/search?q=\')) union select * from users--')
      .expect('status', 500)
      .expect('header', 'content-type', /text\/html/)
      .expect('bodyContains', '
Juice Shop (Express ~')
      .expect('bodyContains', 'SQLITE_ERROR: SELECTs to the left and right of UNION do not have the same number of result columns', done => {})
      .done(done)
  })

  it('GET product search can create UNION SELECT with Users table and fixed columns', done => {
    frisby.get(REST_URL + '/product/search?q=\')) union select \'1\',\'2\',\'3\',\'4\',\'5\',\'6\',\'7\',\'8\' from users--')
      .expect('status', 200)
      .expect('header', 'content-type', /application\/json/)
      .expect('json', 'data.?', {
        id: '1',
        name: '2',
        description: '3',
        price: '4',
        image: '5',
        createdAt: '6',
        updatedAt: '7'
      }).done(done)
  })

  it('GET product search can create UNION SELECT with Users table and required columns', done => {
    frisby.get(REST_URL + '/product/search?q=\')) union select null,id,email,password,null,null,null,null from users--')
      .expect('status', 200)
      .expect('header', 'content-type', /application\/json/)
      .expect('json', 'data.?', {
        name: 1,
        description: 'admin@' + config.get('application.domain'),
        price: insecurity.hash('admin123')
      })
      .expect('json', 'data.?', {
        name: 2,
        description: 'jim@' + config.get('application.domain'),
        price: insecurity.hash('ncc-1701')
      })
      .expect('json', 'data.?', {
        name: 3,
        description: 'bender@' + config.get('application.domain')
        // no check for Bender's password as it might have already been changed by the CSRF test
      })
      .expect('json', 'data.?', {
        name: 4,
        description: 'bjoern.kimminich@googlemail.com',
        price: insecurity.hash('YmpvZXJuLmtpbW1pbmljaEBnb29nbGVtYWlsLmNvbQ==')
      })
      .expect('json', 'data.?', {
        name: 5,
        description: 'ciso@' + config.get('application.domain'),
        price: insecurity.hash('mDLx?94T~1CfVfZMzw@sJ9f?s3L6lbMqE70FfI8^54jbNikY5fymx7c!YbJb')
      })
      .expect('json', 'data.?', {
        name: 6,
        description: 'support@' + config.get('application.domain'),
        price: insecurity.hash('J6aVjTgOpRs$?5l+Zkq2AYnCE@RF§P')
      })
      .done(done)
  })

  it('GET product search cannot select logically deleted christmas special by default', done => {
    frisby.get(REST_URL + '/product/search?q=seasonal%20special%20offer')
      .expect('status', 200)
      .expect('header', 'content-type', /application\/json/)
      .then(res => {
        expect(res.json.data.length).toBe(0)
      })
      .done(done)
  })

  it('GET product search by description cannot select logically deleted christmas special due to forced early where-clause termination', done => {
    frisby.get(REST_URL + '/product/search?q=seasonal%20special%20offer\'))--')
      .expect('status', 200)
      .expect('header', 'content-type', /application\/json/)
      .then(res => {
        expect(res.json.data.length).toBe(0)
      })
      .done(done)
  })

  it('GET product search can select logically deleted christmas special by forcibly commenting out the remainder of where clause', done => {
    frisby.get(REST_URL + '/product/search?q=' + christmasProduct.name + '\'))--')
      .expect('status', 200)
      .expect('header', 'content-type', /application\/json/)
      .then(res => {
        expect(res.json.data.length).toBe(1)
        expect(res.json.data[0].name).toBe(christmasProduct.name)
      })
      .done(done)
  })

  it('GET product search with empty search parameter returns all products', done => {
    frisby.get(API_URL + '/Products')
      .expect('status', 200)
      .expect('header', 'content-type', /application\/json/)
      .then(res => {
        const products = res.json.data
        return frisby.get(REST_URL + '/product/search?q=')
          .expect('status', 200)
          .expect('header', 'content-type', /application\/json/)
          .then(res => {
            expect(res.json.data.length).toBe(products.length)
          })
      }).done(done)
  })

  it('GET product search without search parameter returns all products', done => {
    frisby.get(API_URL + '/Products')
      .expect('status', 200)
      .expect('header', 'content-type', /application\/json/)
      .then(res => {
        const products = res.json.data
        return frisby.get(REST_URL + '/product/search')
          .expect('status', 200)
          .expect('header', 'content-type', /application\/json/)
          .then(res => {
            expect(res.json.data.length).toBe(products.length)
          })
      }).done(done)
  })
})
<commit_msg>Corrected error message<commit_after>const frisby = require('frisby')
const Joi = frisby.Joi
const insecurity = require('../../lib/insecurity')
const config = require('config')

const christmasProduct = config.get('products').filter(product => product.useForChristmasSpecialChallenge)[0]

const tamperingProductId = ((() => {
  const products = config.get('products')
  for (let i = 0; i < products.length; i++) {
    if (products[i].useForProductTamperingChallenge) {
      return i + 1
    }
  }
})())

const API_URL = 'http://localhost:3000/api'
const REST_URL = 'http://localhost:3000/rest'

const authHeader = { 'Authorization': 'Bearer ' + insecurity.authorize(), 'content-type': 'application/json' }
const jsonHeader = { 'content-type': 'application/json' }

describe('/api/Products', () => {
  it('GET all products', done => {
    frisby.get(API_URL + '/Products')
      .expect('status', 200)
      .expect('header', 'content-type', /application\/json/)
      .expect('jsonTypes', 'data.*', {
        id: Joi.number(),
        name: Joi.string(),
        description: Joi.string(),
        price: Joi.number(),
        image: Joi.string()
      }).done(done)
  })

  it('POST new product is forbidden via public API', done => {
    frisby.post(API_URL + '/Products', {
      name: 'Dirt Juice (1000ml)',
      description: 'Made from ugly dirt.',
      price: 0.99,
      image: 'dirt_juice.jpg'
    })
      .expect('status', 401)
      .done(done)
  })

  it('POST new product does not filter XSS attacks', done => {
    frisby.post(API_URL + '/Products', {
      headers: authHeader,
      body: {
        name: 'XSS Juice (42ml)',
        description: '',
        price: 9999.99,
        image: 'xss3juice.jpg'
      }
    })
      .expect('header', 'content-type', /application\/json/)
      .expect('json', 'data', { description: '' })
      .done(done)
  })
})

describe('/api/Products/:id', () => {
  it('GET existing product by id', done => {
    frisby.get(API_URL + '/Products/1')
      .expect('status', 200)
      .expect('header', 'content-type', /application\/json/)
      .expect('jsonTypes', 'data', {
        id: Joi.number(),
        name: Joi.string(),
        description: Joi.string(),
        price: Joi.number(),
        image: Joi.string(),
        createdAt: Joi.string(),
        updatedAt: Joi.string()
      })
      .expect('json', 'data', { id: 1 })
      .done(done)
  })

  it('GET non-existing product by id', done => {
    frisby.get(API_URL + '/Products/4711')
      .expect('status', 404)
      .expect('header', 'content-type', /application\/json/)
      .expect('json', 'message', 'Not Found')
      .done(done)
  })

  it('PUT update existing product is possible due to Missing Function-Level Access Control vulnerability', done => {
    frisby.put(API_URL + '/Products/' + tamperingProductId, {
      header: jsonHeader,
      body: {
        description: 'More...'
      }
    })
      .expect('status', 200)
      .expect('header', 'content-type', /application\/json/)
      .expect('json', 'data', { description: 'More...' })
      .done(done)
  })

  it('PUT update existing product does not filter XSS attacks', done => {
    frisby.put(API_URL + '/Products/1', {
      header: jsonHeader,
      body: {
        description: ""
      }
    })
      .expect('status', 200)
      .expect('header', 'content-type', /application\/json/)
      .expect('json', 'data', { description: "" })
      .done(done)
  })

  it('DELETE existing product is forbidden via public API', done => {
    frisby.del(API_URL + '/Products/1')
      .expect('status', 401)
      .done(done)
  })

  it('DELETE existing product is forbidden via API even when authenticated', done => {
    frisby.del(API_URL + '/Products/1', { headers: authHeader })
      .expect('status', 401)
      .done(done)
  })
})

describe('/rest/product/search', () => {
  it('GET product search with no matches returns no products', done => {
    frisby.get(REST_URL + '/product/search?q=nomatcheswhatsoever')
      .expect('status', 200)
      .expect('header', 'content-type', /application\/json/)
      .then(res => {
        expect(res.json.data.length).toBe(0)
      })
      .done(done)
  })

  it('GET product search with one match returns found product', done => {
    frisby.get(REST_URL + '/product/search?q=o-saft')
      .expect('status', 200)
      .expect('header', 'content-type', /application\/json/)
      .then(res => {
        expect(res.json.data.length).toBe(1)
      })
      .done(done)
  })

  it('GET product search with XSS attack is not blocked', done => {
    frisby.get(REST_URL + '/product/search?q=')
      .expect('status', 200)
      .expect('header', 'content-type', /application\/json/)
      .done(done)
  })

  it('GET product search fails with error message that exposes ins SQL Injection vulnerability', done => {
    frisby.get(REST_URL + '/product/search?q=\';')
      .expect('status', 500)
      .expect('header', 'content-type', /text\/html/)
      .expect('bodyContains', '
Juice Shop (Express ~')
      .expect('bodyContains', 'SQLITE_ERROR: near ";": syntax error')
      .done(done)
  })

  it('GET product search SQL Injection fails from two missing closing parenthesis', done => {
    frisby.get(REST_URL + '/product/search?q=\' union select null,id,email,password,null,null,null from users--')
      .expect('status', 500)
      .expect('header', 'content-type', /text\/html/)
      .expect('bodyContains', '
Juice Shop (Express ~')
      .expect('bodyContains', 'SQLITE_ERROR: near "union": syntax error')
      .done(done)
  })

  it('GET product search SQL Injection fails from one missing closing parenthesis', done => {
    frisby.get(REST_URL + '/product/search?q=\') union select null,id,email,password,null,null,null from users--')
      .expect('status', 500)
      .expect('header', 'content-type', /text\/html/)
      .expect('bodyContains', '
Juice Shop (Express ~')
      .expect('bodyContains', 'SQLITE_ERROR: near "union": syntax error')
      .done(done)
  })

  it('GET product search SQL Injection fails for SELECT * FROM attack due to wrong number of returned columns', done => {
    frisby.get(REST_URL + '/product/search?q=\')) union select * from users--')
      .expect('status', 500)
      .expect('header', 'content-type', /text\/html/)
      .expect('bodyContains', '
Juice Shop (Express ~')
      .expect('bodyContains', 'SQLITE_ERROR: SELECTs to the left and right of UNION do not have the same number of result columns', done => {})
      .done(done)
  })

  it('GET product search can create UNION SELECT with Users table and fixed columns', done => {
    frisby.get(REST_URL + '/product/search?q=\')) union select \'1\',\'2\',\'3\',\'4\',\'5\',\'6\',\'7\',\'8\' from users--')
      .expect('status', 200)
      .expect('header', 'content-type', /application\/json/)
      .expect('json', 'data.?', {
        id: '1',
        name: '2',
        description: '3',
        price: '4',
        image: '5',
        createdAt: '6',
        updatedAt: '7'
      }).done(done)
  })

  it('GET product search can create UNION SELECT with Users table and required columns', done => {
    frisby.get(REST_URL + '/product/search?q=\')) union select null,id,email,password,null,null,null,null from users--')
      .expect('status', 200)
      .expect('header', 'content-type', /application\/json/)
      .expect('json', 'data.?', {
        name: 1,
        description: 'admin@' + config.get('application.domain'),
        price: insecurity.hash('admin123')
      })
      .expect('json', 'data.?', {
        name: 2,
        description: 'jim@' + config.get('application.domain'),
        price: insecurity.hash('ncc-1701')
      })
      .expect('json', 'data.?', {
        name: 3,
        description: 'bender@' + config.get('application.domain')
        // no check for Bender's password as it might have already been changed by the CSRF test
      })
      .expect('json', 'data.?', {
        name: 4,
        description: 'bjoern.kimminich@googlemail.com',
        price: insecurity.hash('YmpvZXJuLmtpbW1pbmljaEBnb29nbGVtYWlsLmNvbQ==')
      })
      .expect('json', 'data.?', {
        name: 5,
        description: 'ciso@' + config.get('application.domain'),
        price: insecurity.hash('mDLx?94T~1CfVfZMzw@sJ9f?s3L6lbMqE70FfI8^54jbNikY5fymx7c!YbJb')
      })
      .expect('json', 'data.?', {
        name: 6,
        description: 'support@' + config.get('application.domain'),
        price: insecurity.hash('J6aVjTgOpRs$?5l+Zkq2AYnCE@RF§P')
      })
      .done(done)
  })

  it('GET product search cannot select logically deleted christmas special by default', done => {
    frisby.get(REST_URL + '/product/search?q=seasonal%20special%20offer')
      .expect('status', 200)
      .expect('header', 'content-type', /application\/json/)
      .then(res => {
        expect(res.json.data.length).toBe(0)
      })
      .done(done)
  })

  it('GET product search by description cannot select logically deleted christmas special due to forced early where-clause termination', done => {
    frisby.get(REST_URL + '/product/search?q=seasonal%20special%20offer\'))--')
      .expect('status', 200)
      .expect('header', 'content-type', /application\/json/)
      .then(res => {
        expect(res.json.data.length).toBe(0)
      })
      .done(done)
  })

  it('GET product search can select logically deleted christmas special by forcibly commenting out the remainder of where clause', done => {
    frisby.get(REST_URL + '/product/search?q=' + christmasProduct.name + '\'))--')
      .expect('status', 200)
      .expect('header', 'content-type', /application\/json/)
      .then(res => {
        expect(res.json.data.length).toBe(1)
        expect(res.json.data[0].name).toBe(christmasProduct.name)
      })
      .done(done)
  })

  it('GET product search with empty search parameter returns all products', done => {
    frisby.get(API_URL + '/Products')
      .expect('status', 200)
      .expect('header', 'content-type', /application\/json/)
      .then(res => {
        const products = res.json.data
        return frisby.get(REST_URL + '/product/search?q=')
          .expect('status', 200)
          .expect('header', 'content-type', /application\/json/)
          .then(res => {
            expect(res.json.data.length).toBe(products.length)
          })
      }).done(done)
  })

  it('GET product search without search parameter returns all products', done => {
    frisby.get(API_URL + '/Products')
      .expect('status', 200)
      .expect('header', 'content-type', /application\/json/)
      .then(res => {
        const products = res.json.data
        return frisby.get(REST_URL + '/product/search')
          .expect('status', 200)
          .expect('header', 'content-type', /application\/json/)
          .then(res => {
            expect(res.json.data.length).toBe(products.length)
          })
      }).done(done)
  })
})
<|endoftext|>


<commit_before>const frisby = require('frisby')
const Joi = frisby.Joi
const insecurity = require('../../lib/insecurity')

const API_URL = 'http://localhost:3000/api'

const authHeader = { 'Authorization': 'Bearer ' + insecurity.authorize(), 'content-type': 'application/json' }

describe('/api/Recycles', () => {
  it('POST new recycle', () => {
    return frisby.post(API_URL + '/Recycles', {
      headers: authHeader,
      body: {
        quantity: 200,
        address: 'Bjoern Kimminich, 123 Juicy Road, Test City',
        isPickup: true,
        date: '2017-05-31'
      }
    })
      .expect('status', 201)
      .expect('header', 'content-type', /application\/json/)
      .expect('jsonTypes', 'data', {
        id: Joi.number(),
        createdAt: Joi.string(),
        updatedAt: Joi.string()
      })
  })

  it('Will not GET all recycles from this endpoint', () => {
    return frisby.get(API_URL + '/Recycles')
      .expect('status', 200)
      .expect('jsonTypes', 'data', {
        err: Joi.string()
      })
  })

  it('PUT update existing recycle is forbidden', () => {
    return frisby.put(API_URL + '/Recycles/1', {
      headers: authHeader,
      body: {
        quantity: 100000
      }
    })
      .expect('status', 401)
  })

  it('DELETE existing recycle is forbidden', () => {
    return frisby.del(API_URL + '/Recycles/1', { headers: authHeader })
      .expect('status', 401)
  })
})
<commit_msg>feat: add test to detect injection<commit_after>const frisby = require('frisby')
const Joi = frisby.Joi
const insecurity = require('../../lib/insecurity')

const API_URL = 'http://localhost:3000/api'

const injectionParam = '{"$lt":200}'

const authHeader = { 'Authorization': 'Bearer ' + insecurity.authorize(), 'content-type': 'application/json' }

describe('/api/Recycles', () => {
  it('POST new recycle', () => {
    return frisby.post(API_URL + '/Recycles', {
      headers: authHeader,
      body: {
        quantity: 200,
        address: 'Bjoern Kimminich, 123 Juicy Road, Test City',
        isPickup: true,
        date: '2017-05-31'
      }
    })
      .expect('status', 201)
      .expect('header', 'content-type', /application\/json/)
      .expect('jsonTypes', 'data', {
        id: Joi.number(),
        createdAt: Joi.string(),
        updatedAt: Joi.string()
      })
  })

  it('Will prevent GET all recycles from this endpoint', () => {
    return frisby.get(API_URL + '/Recycles')
      .expect('status', 200)
      .expect('header', 'content-type', /application\/json/)
      .expect('jsonTypes', 'data', {
        err: 'Sorry, this endpoint is not supported.'
      })
  })

  it('Will check if the injection works and returns the expected recycle', () => {
    return frisby.get(API_URL + '/Recycles/' + injectionParam)
      .expect('status', 200)
      .expect('header', 'content-type', /application\/json/)
      .expect('jsonTypes', 'data[0]', { id: 33 })
  })

  it('PUT update existing recycle is forbidden', () => {
    return frisby.put(API_URL + '/Recycles/1', {
      headers: authHeader,
      body: {
        quantity: 100000
      }
    })
      .expect('status', 401)
  })

  it('DELETE existing recycle is forbidden', () => {
    return frisby.del(API_URL + '/Recycles/1', { headers: authHeader })
      .expect('status', 401)
  })
})
<|endoftext|>


<commit_before>const koala = require('../lib');
const request = require('supertest');

describe('Cache-Control', () => {
  describe('should be available as', () => {
    test('this.cc()', done => {
      const app = koala();
      app.use(function * (next) {
        this.cc(1000);
        this.status = 204;
      });

      request(app.listen())
        .get('/')
        .expect(204)
        .expect('Cache-Control', 'public, max-age=1')
        .end(done);
    });

    test('this.cacheControl()', done => {
      const app = koala();
      app.use(function * (next) {
        this.cacheControl(1000);
        this.status = 204;
      });

      request(app.listen())
        .get('/')
        .expect(204)
        .expect('Cache-Control', 'public, max-age=1')
        .end(done);
    });

    test('this.response.cc()', done => {
      const app = koala();
      app.use(function * (next) {
        this.response.cc(1000);
        this.status = 204;
      });

      request(app.listen())
        .get('/')
        .expect(204)
        .expect('Cache-Control', 'public, max-age=1')
        .end(done);
    });
  });

  describe('when the value is a number', () => {
    test('should set "public, max-age="', done => {
      const app = koala();
      app.use(function * (next) {
        this.response.cacheControl(1000000);
        this.status = 204;
      });

      request(app.listen())
        .get('/')
        .expect(204)
        .expect('Cache-Control', 'public, max-age=1000')
        .end(done);
    });
  });

  describe('when the value is a time string', () => {
    test('should set "public, max-age="', done => {
      const app = koala();
      app.use(function * (next) {
        this.response.cacheControl('1 hour');
        this.status = 204;
      });

      request(app.listen())
        .get('/')
        .expect(204)
        .expect('Cache-Control', 'public, max-age=3600')
        .end(done);
    });
  });

  describe('when the value is "false"', () => {
    test('should set "private, no-cache"', done => {
      const app = koala();
      app.use(function * (next) {
        this.response.cacheControl(false);
        this.status = 204;
      });

      request(app.listen())
        .get('/')
        .expect(204)
        .expect('Cache-Control', 'private, no-cache')
        .end(done);
    });
  });

  describe('when the value is a string', () => {
    test('should juset set it', done => {
      const app = koala();
      app.use(function * (next) {
        this.response.cacheControl('foo');
        this.status = 204;
      });

      request(app.listen())
        .get('/')
        .expect(204)
        .expect('Cache-Control', 'foo')
        .end(done);
    });
  });

  describe('when the value is anything else', () => {
    test('should throw', done => {
      const app = koala();
      app.use(function * (next) {
        this.response.cacheControl(true);
        this.status = 204;
      });

      request(app.listen())
        .get('/')
        .expect(500, done);
    });
  });
});
<commit_msg>Fix typo on test name<commit_after>const koala = require('../lib');
const request = require('supertest');

describe('Cache-Control', () => {
  describe('should be available as', () => {
    test('this.cc()', done => {
      const app = koala();
      app.use(function * (next) {
        this.cc(1000);
        this.status = 204;
      });

      request(app.listen())
        .get('/')
        .expect(204)
        .expect('Cache-Control', 'public, max-age=1')
        .end(done);
    });

    test('this.cacheControl()', done => {
      const app = koala();
      app.use(function * (next) {
        this.cacheControl(1000);
        this.status = 204;
      });

      request(app.listen())
        .get('/')
        .expect(204)
        .expect('Cache-Control', 'public, max-age=1')
        .end(done);
    });

    test('this.response.cc()', done => {
      const app = koala();
      app.use(function * (next) {
        this.response.cc(1000);
        this.status = 204;
      });

      request(app.listen())
        .get('/')
        .expect(204)
        .expect('Cache-Control', 'public, max-age=1')
        .end(done);
    });
  });

  describe('when the value is a number', () => {
    test('should set "public, max-age="', done => {
      const app = koala();
      app.use(function * (next) {
        this.response.cacheControl(1000000);
        this.status = 204;
      });

      request(app.listen())
        .get('/')
        .expect(204)
        .expect('Cache-Control', 'public, max-age=1000')
        .end(done);
    });
  });

  describe('when the value is a time string', () => {
    test('should set "public, max-age="', done => {
      const app = koala();
      app.use(function * (next) {
        this.response.cacheControl('1 hour');
        this.status = 204;
      });

      request(app.listen())
        .get('/')
        .expect(204)
        .expect('Cache-Control', 'public, max-age=3600')
        .end(done);
    });
  });

  describe('when the value is "false"', () => {
    test('should set "private, no-cache"', done => {
      const app = koala();
      app.use(function * (next) {
        this.response.cacheControl(false);
        this.status = 204;
      });

      request(app.listen())
        .get('/')
        .expect(204)
        .expect('Cache-Control', 'private, no-cache')
        .end(done);
    });
  });

  describe('when the value is a string', () => {
    test('should just set it', done => {
      const app = koala();
      app.use(function * (next) {
        this.response.cacheControl('foo');
        this.status = 204;
      });

      request(app.listen())
        .get('/')
        .expect(204)
        .expect('Cache-Control', 'foo')
        .end(done);
    });
  });

  describe('when the value is anything else', () => {
    test('should throw', done => {
      const app = koala();
      app.use(function * (next) {
        this.response.cacheControl(true);
        this.status = 204;
      });

      request(app.listen())
        .get('/')
        .expect(500, done);
    });
  });
});
<|endoftext|>


<commit_before>(function () {
"use strict";

var app = angular.module('myApp');

app.directive('whenScrolled', function() {
    return function(scope, elem, attr) {
        $(window).bind('scroll', function() {
	    var docViewBottom = $(window).scrollTop() + $(window).height();
	    var elemBottom = $(elem).offset().top + $(elem).height();
	    if (elemBottom === 0) return; // why? in some cases, the event is triggered and the height/position is unknown (on chromium at least)
	    //var t = elemBottom + " <= " + docViewBottom
	    //$("#debug").text(t);
	    if ((elemBottom <= docViewBottom)) {
		//$("#debug").text(t + " scrolled into view");
                scope.$apply(attr.whenScrolled);
            }
        });
    };
});

app.directive('displayRequiredIfNeeded', function ($parse) {
    return { restrict: 'A',
	     template: 'Required',
	     scope: { inputName: '=' }
	   };
});

app.directive('myValidator', function () {
  return {
    restrict: 'A',
    require: 'ngModel', // controller to be passed into directive linking function
    link: function (scope, elem, attr, ctrl) {
	var checkers = scope.$eval(attr.myValidator);
	ctrl.$parsers.push(function (viewValue) {
	    angular.forEach(checkers, function (checker, checkName) {
		ctrl.$setValidity(checkName, checker(viewValue));
	    });
	    return viewValue;
        });
    }
   };
});

app.directive('autocompleteUserOrGroup', function (globals) {
  var searchUserURL = globals.wsgroupsURL + '/searchUserCAS';
  var searchGroupURL = globals.wsgroupsURL + '/searchGroup';
  return {
    restrict: 'A',
    require: 'ngModel',
    link: function (scope, el, attr, ngModel) {
	var select = function (event, ui) {
	    // NB: this event is called before the selected value is set in the "input"

	    ui.item.id = ui.item.value;
	    ui.item.name = ui.item.label;

	    $(el).val(ui.item.label);

            scope.$apply(function () {
                ngModel.$setViewValue(ui.item);
            });
	    scope.$apply(attr.onSelect);
	    return false;
        };
	var params = { select: select };
	if (attr.autocompleteUserOrGroup === 'user') {
            $(el).autocompleteUser(searchUserURL, params);
	} else {
            $(el).autocompleteGroup(searchGroupURL, params);
	}
    }
  };
});

app.directive('myAutocomplete', function (globals) {
  return {
    restrict: 'A',
    replace: true,
    // the scope could be the following (can't work for ng-model, so simple dynamic templating is used)
    //scope: { onSelect: '&', doSearch: '&', ng-model: '=' },
    template: function(element, attrs) {
	if (globals.wsgroupsURL) {
	    return '<input type="text" autocomplete-user-or-group="' + attrs.myAutocomplete + '">';
	} else {
	    var typeahead = 'e as e.name for e in ' + attrs.doSearch;
            return '<input type="text" typeahead="' + typeahead + '" typeahead-on-select="' + attrs.onSelect + '" >';
	}
    }
  };
});

app.directive('myInclude', function (globals) {
  return {
    restrict: 'A',
    replace: true,
    template: function(element, attrs) {
	var absoluteURL = globals.baseURL + '/partials/' + attrs.myInclude;
	return '

';
    }
  };
});

})();<commit_msg>wsgroups: activate navigation in autocompleteGroup<commit_after>(function () {
"use strict";

var app = angular.module('myApp');

app.directive('whenScrolled', function() {
    return function(scope, elem, attr) {
        $(window).bind('scroll', function() {
	    var docViewBottom = $(window).scrollTop() + $(window).height();
	    var elemBottom = $(elem).offset().top + $(elem).height();
	    if (elemBottom === 0) return; // why? in some cases, the event is triggered and the height/position is unknown (on chromium at least)
	    //var t = elemBottom + " <= " + docViewBottom
	    //$("#debug").text(t);
	    if ((elemBottom <= docViewBottom)) {
		//$("#debug").text(t + " scrolled into view");
                scope.$apply(attr.whenScrolled);
            }
        });
    };
});

app.directive('displayRequiredIfNeeded', function ($parse) {
    return { restrict: 'A',
	     template: 'Required',
	     scope: { inputName: '=' }
	   };
});

app.directive('myValidator', function () {
  return {
    restrict: 'A',
    require: 'ngModel', // controller to be passed into directive linking function
    link: function (scope, elem, attr, ctrl) {
	var checkers = scope.$eval(attr.myValidator);
	ctrl.$parsers.push(function (viewValue) {
	    angular.forEach(checkers, function (checker, checkName) {
		ctrl.$setValidity(checkName, checker(viewValue));
	    });
	    return viewValue;
        });
    }
   };
});

app.directive('autocompleteUserOrGroup', function (globals) {
  var searchUserURL = globals.wsgroupsURL + '/searchUserCAS';
  var searchGroupURL = globals.wsgroupsURL + '/searchGroup';
  var subAndSuperGroupsURL = globals.wsgroupsURL + '/getSubAndSuperGroups';

  return {
    restrict: 'A',
    require: 'ngModel',
    link: function (scope, el, attr, ngModel) {
	var select = function (event, ui) {
	    // NB: this event is called before the selected value is set in the "input"

	    ui.item.id = ui.item.value;
	    ui.item.name = ui.item.label;

	    $(el).val(ui.item.label);

            scope.$apply(function () {
                ngModel.$setViewValue(ui.item);
            });
	    scope.$apply(attr.onSelect);
	    return false;
        };
	var params = { select: select };
	if (attr.autocompleteUserOrGroup === 'user') {
            $(el).autocompleteUser(searchUserURL, params);
	} else {
	    params.subAndSuperGroupsURL = subAndSuperGroupsURL;
            $(el).autocompleteGroup(searchGroupURL, params);
	}
    }
  };
});

app.directive('myAutocomplete', function (globals) {
  return {
    restrict: 'A',
    replace: true,
    // the scope could be the following (can't work for ng-model, so simple dynamic templating is used)
    //scope: { onSelect: '&', doSearch: '&', ng-model: '=' },
    template: function(element, attrs) {
	if (globals.wsgroupsURL) {
	    return '<input type="text" autocomplete-user-or-group="' + attrs.myAutocomplete + '">';
	} else {
	    var typeahead = 'e as e.name for e in ' + attrs.doSearch;
            return '<input type="text" typeahead="' + typeahead + '" typeahead-on-select="' + attrs.onSelect + '" >';
	}
    }
  };
});

app.directive('myInclude', function (globals) {
  return {
    restrict: 'A',
    replace: true,
    template: function(element, attrs) {
	var absoluteURL = globals.baseURL + '/partials/' + attrs.myInclude;
	return '

';
    }
  };
});

})();<|endoftext|>