<commit_before>const version = require('./package.json').version
const port = 8081

const express = require('express')
const favicon = require('serve-favicon')
const User = require('./app/models/User')
const Channel = require('./app/models/Channel')
const Store = require('./app/Store')

const bodyParser = require('body-parser')
const app = express()

app.use(favicon(__dirname + '/public/favicon.ico'))
app.use(bodyParser.urlencoded({ extended: false }))
app.use(bodyParser.json())
app.set('views', './public')
app.set('view engine', 'ejs')

let store = new Store()

// API

app.get('/', function(req, res) {
    res.render('api.ejs', { version: version })
})

// Info

app.get('/info/', function(req, res) {
    res.send({ 'version':version })
})

// List users

app.get('/users/', function(req, res) {
    res.send(Object.keys(store.users).map(id => store.users[id].getPublicUser()))
})

// Register

app.post('/users/register/:nick/', function(req, res) {
    const user = new User(store.getAvailableNick(req.params.nick))
    store.addUser(user)
    res.send(user)
})

// Disconnect

app.delete('/user/:id/disconnect/', function(req, res) {
    const user = store.getUser(req.params.id)
    if (user === undefined) {
        res.status(404).send({ error: 'Unknown user ID' })
        return
    }
	Object.keys(user.channels).forEach(name => {
            notice({ type: 'channelLeave', nick: user.nick, channel: store.getChannel(name) })
            const channel = store.getChannel(name)
            if (!channel.keep && store.getUsersByChannel(channel.name).length == 1) {
                store.removeChannel(channel.name)
            }
    })
    store.removeUser(user.id)
    res.send({ status: 'Successfully disconnected from the server' })
})

// Whois

app.get('/users/whois/:nick/', function(req, res) {
    const id = Object.keys(store.users).find(id => store.users[id].nick === req.params.nick)
	const user = store.getUser(id)
    if (user === undefined) {
        res.status(404).send({ 'error': 'Unknown nick' })
    }
    else {
        res.send(user.getPublicUser())
    }
})

// List channels

app.get('/channels/', function(req, res){
    res.send(Object.keys(store.channels).map(name => store.channels[name]))
})

// Join channel

app.put('/user/:id/channels/:channel/join/', function(req, res) {
    const user = store.getUser(req.params.id)
    if (user === undefined) {
        res.status(404).send({ error: 'Unknown user ID' })
        return
    }
    let channel = store.getChannel(req.params.channel)
    if (channel === undefined) {
        channel = new Channel(req.params.channel, user.nick)
        store.addChannel(channel)
    }
    user.channels[channel.name] = channel
    notice({ type: 'channelJoin', nick: user.nick, channel: channel })
    res.send({ channel: channel, users: store.getUsersByChannel(channel.name, true) })
})

// Talk in channel

app.put('/user/:id/channels/:channel/say/', function(req, res) {
    const user = store.getUser(req.params.id)
    if (user === undefined) {
        res.status(404).send({ error: 'Unknown user ID' })
        return
    }
    let channel = store.getChannel(req.params.channel)
    if (channel === undefined) {
        channel = new Channel(req.params.channel)
        store.addChannel(channel)
    }

    if (user.channels[channel.name] === undefined) {
        user.channels[channel.name] = channel
    }
    const message = req.body.message

    notice({ type: 'channelMessage', nick: user.nick, channel: channel, message: message })
    res.send({ status: 'Message sent correctly', message: message })
})

// Change channel description

app.put('/user/:id/channels/:channel/description/', function(req, res) {
    const user = store.getUser(req.params.id)
    if (user === undefined) {
        res.status(404).send({ error: 'Unknown user ID' })
        return
    }
    const channel = store.getChannel(req.params.channel)
    if (channel === undefined) {
        res.status(404).send({ error: 'Unknown channel' })
        return
    }
    const description = req.body.description

    channel.description = description

    notice({ type: 'channelDescription', nick: user.nick, channel: channel })
    res.send({ status: 'Changing the description', channel: channel, description: description })
})

// Keep channel

app.put('/user/:id/channels/:channel/keep', function(req, res) {
    const user = store.getUser(req.params.id)
    if (user === undefined) {
        res.status(404).send({ error: 'Unknown user ID' })
        return
    }
    const channel = store.getChannel(req.params.channel)
    if (channel === undefined) {
        res.status(404).send({ error: 'Unknown channel' })
        return
    }
    if (channel.owner !== user.nick) {
        res.status(404).send({ error: 'You don\'t own the channel' })
        return
    }
    const keep = req.body.keep

    channel.keep = keep == true

    if (!channel.keep && store.getUsersByChannel(channel.name).length == 0) {
        store.removeChannel(channel.name)
    }

    notice({ type: 'channelKeep', nick: user.nick, channel: channel })
    res.send({ status: 'Changing the persistence', channel: channel })
})

// Give channel ownership

app.put('/user/:id/channels/:channel/owner/:nick/', function(req, res) {
    const user = store.getUser(req.params.id)
    if (user === undefined) {
        res.status(404).send({ error: 'Unknown user ID' })
        return
    }
    const channel = store.getChannel(req.params.channel)
    if (channel === undefined) {
        res.status(404).send({ error: 'Unknown channel' })
        return
    }
    const recipient = store.getUserByNick(req.params.nick)
    if (recipient === undefined) {
        res.status(404).send({ error: 'Unknown username' })
        return
    }
    if (channel.owner !== user.nick) {
        res.status(404).send({ error: 'You don\'t own the channel' })
        return
    }
    channel.owner = recipient.nick

    notice({ type:'channelOwner', channel: channel, nick: user.nick })
    res.send({ status: 'Ownership transfered', channel: channel })
})

// Leave channel

app.delete('/user/:id/channels/:channel/leave/', function(req, res) {
    const user = store.getUser(req.params.id)
    if (user === undefined) {
        res.status(404).send({ error: 'Unknow user ID' })
        return
    }
    const channel = store.getChannel(req.params.channel)
    if (user.channels[channel.name] !== undefined)
    {
        delete user.channels[channel.name]
        if (channel !== undefined) {
            if (!channel.keep && store.getUsersByChannel(channel.name).length == 0) {
                store.removeChannel(channel.name)
            }
            notice({ type: 'channelLeave', nick: user.nick, channel: channel })
        }
    }
    else {
        res.send(404).send({ error: "Not in channel, can't leave." })
    }
    res.send({ status: 'Leaving the channel', channel: channel })
})

// Send private message

app.put('/user/:id/message/:nick/', function(req, res) {
    const sender = store.getUser(req.params.id)
    const recipient = store.getUserByNick(req.params.nick)
    const message = req.body.message
    if (sender === undefined) {
        res.status(404).send({ error: 'Unknown user ID'})
        return
    }
    if (recipient === undefined) {
        res.status(404).send({ error: 'Unknown username'})
        return
    }

    notice({ type: 'privateMessage', sender: sender.nick, recipient: recipient.nick, message: message})
    res.send({ status: 'Private message sent correctly', message: message, recipient: recipient })
})

// Fetch notices

app.get('/user/:id/notices/', function(req, res) {
    const user = store.getUser(req.params.id)
    if (user === undefined) {
        res.status(404).send({ error: 'Unknown user ID' })
        return
    }
    user.update()
    const idlingUsers = store.getIdlingUsers()
	idlingUsers.forEach(user => {
        Object.keys(user.channels).forEach(channel => {
            notice({ type: 'channelLeave', nick: user.nick, channel: channel })
            if (store.getUsersByChannel(channel).length == 1) {
                store.removeChannel(channel)
            }
        })
        store.removeUser(user.id)
    })
    res.send(user.notices)
    user.notices = []
})

// Notice

function notice(message) {
    message.time = new Date().toJSON()

    switch (message.type) {
        case 'channelJoin':
        case 'channelMessage':
        case 'channelLeave':
        case 'channelDescription':
        case 'channelKeep':
        case 'channelOwner':
            const channel = store.getChannel(message.channel.name)
            if (channel !== undefined) {
                const users = store.getUsersByChannel(channel.name, false)
                users.forEach(user => user.notices.push(message))
            }
            break

        case 'privateMessage':
            const recipient = store.getUserByNick(message.recipient)
            if (recipient !== undefined) {
                recipient.notices.push(message)
            }
            break

        default:
    }
}

// Error handling

function error(req, res) {
     res.status(404).send({error: "Unknown route or method."})
	 console.log('ERROR accessing ' + req.method + ' ' + req.path)
}

app.get('*', error)
app.post('*', error)
app.put('*', error)
app.delete('*', error)

// Server listening

if (__filename == process.argv[1]) {
    console.info('Diskuss v' + version + ' started on port ' + port)
    app.listen(port)
} else {
    module.exports = app
    module.exports.reset = function() {
        store = new Store()
    }
}
<commit_msg>Added support for CORS<commit_after>const version = require('./package.json').version
const port = 8081

const express = require('express')
const cors = require('cors')
const favicon = require('serve-favicon')
const User = require('./app/models/User')
const Channel = require('./app/models/Channel')
const Store = require('./app/Store')

const bodyParser = require('body-parser')
const app = express()

app.use(favicon(__dirname + '/public/favicon.ico'))
app.use(bodyParser.urlencoded({ extended: false }))
app.use(bodyParser.json())
app.use(cors())
app.set('views', './public')
app.set('view engine', 'ejs')

let store = new Store()

// API

app.get('/', function(req, res) {
    res.render('api.ejs', { version: version })
})

// Info

app.get('/info/', function(req, res) {
    res.send({ 'version':version })
})

// List users

app.get('/users/', function(req, res) {
    res.send(Object.keys(store.users).map(id => store.users[id].getPublicUser()))
})

// Register

app.post('/users/register/:nick/', function(req, res) {
    const user = new User(store.getAvailableNick(req.params.nick))
    store.addUser(user)
    res.send(user)
})

// Disconnect

app.delete('/user/:id/disconnect/', function(req, res) {
    const user = store.getUser(req.params.id)
    if (user === undefined) {
        res.status(404).send({ error: 'Unknown user ID' })
        return
    }
	Object.keys(user.channels).forEach(name => {
            notice({ type: 'channelLeave', nick: user.nick, channel: store.getChannel(name) })
            const channel = store.getChannel(name)
            if (!channel.keep && store.getUsersByChannel(channel.name).length == 1) {
                store.removeChannel(channel.name)
            }
    })
    store.removeUser(user.id)
    res.send({ status: 'Successfully disconnected from the server' })
})

// Whois

app.get('/users/whois/:nick/', function(req, res) {
    const id = Object.keys(store.users).find(id => store.users[id].nick === req.params.nick)
	const user = store.getUser(id)
    if (user === undefined) {
        res.status(404).send({ 'error': 'Unknown nick' })
    }
    else {
        res.send(user.getPublicUser())
    }
})

// List channels

app.get('/channels/', function(req, res){
    res.send(Object.keys(store.channels).map(name => store.channels[name]))
})

// Join channel

app.put('/user/:id/channels/:channel/join/', function(req, res) {
    const user = store.getUser(req.params.id)
    if (user === undefined) {
        res.status(404).send({ error: 'Unknown user ID' })
        return
    }
    let channel = store.getChannel(req.params.channel)
    if (channel === undefined) {
        channel = new Channel(req.params.channel, user.nick)
        store.addChannel(channel)
    }
    user.channels[channel.name] = channel
    notice({ type: 'channelJoin', nick: user.nick, channel: channel })
    res.send({ channel: channel, users: store.getUsersByChannel(channel.name, true) })
})

// Talk in channel

app.put('/user/:id/channels/:channel/say/', function(req, res) {
    const user = store.getUser(req.params.id)
    if (user === undefined) {
        res.status(404).send({ error: 'Unknown user ID' })
        return
    }
    let channel = store.getChannel(req.params.channel)
    if (channel === undefined) {
        channel = new Channel(req.params.channel)
        store.addChannel(channel)
    }

    if (user.channels[channel.name] === undefined) {
        user.channels[channel.name] = channel
    }
    const message = req.body.message

    notice({ type: 'channelMessage', nick: user.nick, channel: channel, message: message })
    res.send({ status: 'Message sent correctly', message: message })
})

// Change channel description

app.put('/user/:id/channels/:channel/description/', function(req, res) {
    const user = store.getUser(req.params.id)
    if (user === undefined) {
        res.status(404).send({ error: 'Unknown user ID' })
        return
    }
    const channel = store.getChannel(req.params.channel)
    if (channel === undefined) {
        res.status(404).send({ error: 'Unknown channel' })
        return
    }
    const description = req.body.description

    channel.description = description

    notice({ type: 'channelDescription', nick: user.nick, channel: channel })
    res.send({ status: 'Changing the description', channel: channel, description: description })
})

// Keep channel

app.put('/user/:id/channels/:channel/keep', function(req, res) {
    const user = store.getUser(req.params.id)
    if (user === undefined) {
        res.status(404).send({ error: 'Unknown user ID' })
        return
    }
    const channel = store.getChannel(req.params.channel)
    if (channel === undefined) {
        res.status(404).send({ error: 'Unknown channel' })
        return
    }
    if (channel.owner !== user.nick) {
        res.status(404).send({ error: 'You don\'t own the channel' })
        return
    }
    const keep = req.body.keep

    channel.keep = keep == true

    if (!channel.keep && store.getUsersByChannel(channel.name).length == 0) {
        store.removeChannel(channel.name)
    }

    notice({ type: 'channelKeep', nick: user.nick, channel: channel })
    res.send({ status: 'Changing the persistence', channel: channel })
})

// Give channel ownership

app.put('/user/:id/channels/:channel/owner/:nick/', function(req, res) {
    const user = store.getUser(req.params.id)
    if (user === undefined) {
        res.status(404).send({ error: 'Unknown user ID' })
        return
    }
    const channel = store.getChannel(req.params.channel)
    if (channel === undefined) {
        res.status(404).send({ error: 'Unknown channel' })
        return
    }
    const recipient = store.getUserByNick(req.params.nick)
    if (recipient === undefined) {
        res.status(404).send({ error: 'Unknown username' })
        return
    }
    if (channel.owner !== user.nick) {
        res.status(404).send({ error: 'You don\'t own the channel' })
        return
    }
    channel.owner = recipient.nick

    notice({ type:'channelOwner', channel: channel, nick: user.nick })
    res.send({ status: 'Ownership transfered', channel: channel })
})

// Leave channel

app.delete('/user/:id/channels/:channel/leave/', function(req, res) {
    const user = store.getUser(req.params.id)
    if (user === undefined) {
        res.status(404).send({ error: 'Unknow user ID' })
        return
    }
    const channel = store.getChannel(req.params.channel)
    if (user.channels[channel.name] !== undefined)
    {
        delete user.channels[channel.name]
        if (channel !== undefined) {
            if (!channel.keep && store.getUsersByChannel(channel.name).length == 0) {
                store.removeChannel(channel.name)
            }
            notice({ type: 'channelLeave', nick: user.nick, channel: channel })
        }
    }
    else {
        res.send(404).send({ error: "Not in channel, can't leave." })
    }
    res.send({ status: 'Leaving the channel', channel: channel })
})

// Send private message

app.put('/user/:id/message/:nick/', function(req, res) {
    const sender = store.getUser(req.params.id)
    const recipient = store.getUserByNick(req.params.nick)
    const message = req.body.message
    if (sender === undefined) {
        res.status(404).send({ error: 'Unknown user ID'})
        return
    }
    if (recipient === undefined) {
        res.status(404).send({ error: 'Unknown username'})
        return
    }

    notice({ type: 'privateMessage', sender: sender.nick, recipient: recipient.nick, message: message})
    res.send({ status: 'Private message sent correctly', message: message, recipient: recipient })
})

// Fetch notices

app.get('/user/:id/notices/', function(req, res) {
    const user = store.getUser(req.params.id)
    if (user === undefined) {
        res.status(404).send({ error: 'Unknown user ID' })
        return
    }
    user.update()
    const idlingUsers = store.getIdlingUsers()
	idlingUsers.forEach(user => {
        Object.keys(user.channels).forEach(channel => {
            notice({ type: 'channelLeave', nick: user.nick, channel: channel })
            if (store.getUsersByChannel(channel).length == 1) {
                store.removeChannel(channel)
            }
        })
        store.removeUser(user.id)
    })
    res.send(user.notices)
    user.notices = []
})

// Notice

function notice(message) {
    message.time = new Date().toJSON()

    switch (message.type) {
        case 'channelJoin':
        case 'channelMessage':
        case 'channelLeave':
        case 'channelDescription':
        case 'channelKeep':
        case 'channelOwner':
            const channel = store.getChannel(message.channel.name)
            if (channel !== undefined) {
                const users = store.getUsersByChannel(channel.name, false)
                users.forEach(user => user.notices.push(message))
            }
            break

        case 'privateMessage':
            const recipient = store.getUserByNick(message.recipient)
            if (recipient !== undefined) {
                recipient.notices.push(message)
            }
            break

        default:
    }
}

// Error handling

function error(req, res) {
     res.status(404).send({error: "Unknown route or method."})
	 console.log('ERROR accessing ' + req.method + ' ' + req.path)
}

app.get('*', error)
app.post('*', error)
app.put('*', error)
app.delete('*', error)

// Server listening

if (__filename == process.argv[1]) {
    console.info('Diskuss v' + version + ' started on port ' + port)
    app.listen(port)
} else {
    module.exports = app
    module.exports.reset = function() {
        store = new Store()
    }
}
<|endoftext|>


<commit_before>// Load modules

var Any = require('./any');
var Errors = require('./errors');
var Hoek = require('hoek');

// Declare internals

var internals = {};

internals.Number = function () {

    Any.call(this);
    this._type = 'number';
};

Hoek.inherits(internals.Number, Any);

internals.Number.prototype._base = function (value, state, options) {

    var result = {
        errors: null,
        value: value
    };

    if (typeof value === 'string' &&
        options.convert) {

        var number = parseFloat(value);
        result.value = (isNaN(number) || !isFinite(value)) ? NaN : number;
    }

    result.errors = (typeof result.value === 'number' && !isNaN(result.value)) ? null : Errors.create('number.base', null, state, options);
    return result;
};

internals.Number.prototype.min = function (limit) {

    Hoek.assert(Hoek.isInteger(limit), 'limit must be an integer');

    return this._test('min', limit, function (value, state, options) {

        if (value >= limit) {
            return null;
        }

        return Errors.create('number.min', { limit: limit }, state, options);
    });
};

internals.Number.prototype.max = function (limit) {

    Hoek.assert(Hoek.isInteger(limit), 'limit must be an integer');

    return this._test('max', limit, function (value, state, options) {

        if (value <= limit) {
            return null;
        }

        return Errors.create('number.max', { limit: limit }, state, options);
    });
};

internals.Number.prototype.integer = function () {

    return this._test('integer', undefined, function (value, state, options) {

        return Hoek.isInteger(value) ? null : Errors.create('number.integer', null, state, options);
    });
};

internals.Number.prototype.negative = function () {

    return this._test('negative', undefined, function (value, state, options) {

        if (value < 0) {
            return null;
        }

        return Errors.create('number.negative', null, state, options);
    });
};

internals.Number.prototype.positive = function () {

    return this._test('positive', undefined, function (value, state, options) {

        if (value > 0) {
            return null;
        }

        return Errors.create('number.positive', null, state, options);
    });
};

internals.Number.prototype.precision = function (limit) {

    Hoek.assert(Hoek.isInteger(limit), 'limit must be an integer');

    return this._test('precision', limit, function (value, state, options){

        var decimalPlaces = value.toString().match(/(?:\.(\d+))?(?:[eE]([+-]?\d+))?$/);
        var numDecimalPlaces = Math.max((decimalPlaces[1] ? decimalPlaces[1].length : 0) - (decimalPlaces[2] ? +decimalPlaces[2] : 0), 0);
        if (numDecimalPlaces <= limit) {
            return null;
        }

        return Errors.create('number.precision', {limit: limit}, state, options);
    });
};

module.exports = new internals.Number();
<commit_msg>Cleanup for #427<commit_after>// Load modules

var Any = require('./any');
var Errors = require('./errors');
var Hoek = require('hoek');

// Declare internals

var internals = {};

internals.Number = function () {

    Any.call(this);
    this._type = 'number';
};

Hoek.inherits(internals.Number, Any);

internals.Number.prototype._base = function (value, state, options) {

    var result = {
        errors: null,
        value: value
    };

    if (typeof value === 'string' &&
        options.convert) {

        var number = parseFloat(value);
        result.value = (isNaN(number) || !isFinite(value)) ? NaN : number;
    }

    result.errors = (typeof result.value === 'number' && !isNaN(result.value)) ? null : Errors.create('number.base', null, state, options);
    return result;
};

internals.Number.prototype.min = function (limit) {

    Hoek.assert(Hoek.isInteger(limit), 'limit must be an integer');

    return this._test('min', limit, function (value, state, options) {

        if (value >= limit) {
            return null;
        }

        return Errors.create('number.min', { limit: limit }, state, options);
    });
};

internals.Number.prototype.max = function (limit) {

    Hoek.assert(Hoek.isInteger(limit), 'limit must be an integer');

    return this._test('max', limit, function (value, state, options) {

        if (value <= limit) {
            return null;
        }

        return Errors.create('number.max', { limit: limit }, state, options);
    });
};

internals.Number.prototype.integer = function () {

    return this._test('integer', undefined, function (value, state, options) {

        return Hoek.isInteger(value) ? null : Errors.create('number.integer', null, state, options);
    });
};

internals.Number.prototype.negative = function () {

    return this._test('negative', undefined, function (value, state, options) {

        if (value < 0) {
            return null;
        }

        return Errors.create('number.negative', null, state, options);
    });
};

internals.Number.prototype.positive = function () {

    return this._test('positive', undefined, function (value, state, options) {

        if (value > 0) {
            return null;
        }

        return Errors.create('number.positive', null, state, options);
    });
};

internals.precisionRx = /(?:\.(\d+))?(?:[eE]([+-]?\d+))?$/;

internals.Number.prototype.precision = function (limit) {

    Hoek.assert(Hoek.isInteger(limit), 'limit must be an integer');

    return this._test('precision', limit, function (value, state, options){

        var places = value.toString().match(internals.precisionRx);
        var decimals = Math.max((places[1] ? places[1].length : 0) - (places[2] ? parseInt(places[2], 10) : 0), 0);
        if (decimals <= limit) {
            return null;
        }

        return Errors.create('number.precision', {limit: limit}, state, options);
    });
};

module.exports = new internals.Number();
<|endoftext|>


<commit_before>var say = require('../../utils/say');
var global = require('../../global');

module.exports = function (unitPaymentCorrection) {

    var items = unitPaymentCorrection.items.map((item) => {
        return {
            quantity: item.quantity,
            uom: item.uom,
            product: item.product,
            pricePerUnit: item.pricePerUnit,
            priceTotal: item.priceTotal,
            prNo: item.purchaseOrder.purchaseRequest.no
        };
    });

    items = [].concat.apply([], items);
    var iso = "FM-PB-00-06-015";
    var currency = unitPaymentCorrection.items.find(r => true).currency.code;
    var urDates = unitPaymentCorrection.unitPaymentOrder.items.map(unitPaymentOrderItem => {
        return new Date(unitPaymentOrderItem.unitReceiptNote.date)
    })
    var sjDate = Math.max.apply(null, urDates);

    var locale = global.config.locale;

    var moment = require('moment');
    moment.locale(locale.name);

    var numberLocaleOptions = {
        style: 'decimal',
        maximumFractionDigits: 4

    };
    var header = [
        {
            columns: [
                {
                    width: '40%',
                    text: 'PT DAN LIRIS',
                    style: ['size15', 'bold', 'left']
                }, {
                    width: '60%',
                    text: 'NOTA DEBET',
                    style: ['size15', 'bold', 'left']

                }]
        }, {
            columns: [
                {
                    width: '70%',
                    text: 'BANARAN, GROGOL, SUKOHARJO',
                    style: ['size08']
                }, {
                    width: '30%',
                    stack: [
                        {
                            text: iso,
                            style: ['size09', 'bold']
                        },
                        `SUKOHARJO, ${moment(unitPaymentCorrection.date).format(locale.date.format)}`,
                        `(${unitPaymentCorrection.unitPaymentOrder.supplier.code}) ${unitPaymentCorrection.unitPaymentOrder.supplier.name}`,
                        `${unitPaymentCorrection.unitPaymentOrder.supplier.address}`],
                    alignment: 'left',
                    style: ['size08']

                }]
        }, '\n', {
            columns: [
                {
                    stack: [{
                        columns: [{
                            width: '35%',
                            text: "Retur/Potongan"
                        }, {
                                width: '5%',
                                text: ":"
                            }, {
                                width: '*',
                                text: unitPaymentCorrection.unitPaymentOrder.category.name
                            }],
                        style: ['size08']
                    }, {
                            columns: [{
                                width: '35%',
                                text: "Untuk"
                            }, {
                                    width: '5%',
                                    text: ":"
                                }, {
                                    width: '*',
                                    text: unitPaymentCorrection.unitPaymentOrder.division.name
                                }],
                            style: ['size08']
                        }
                    ]
                }, {
                    width: '20%',
                    text: ''
                }, {
                    width: '30%',
                    text: `Nomor ${unitPaymentCorrection.no}`,
                    style: ['size09', 'left', 'bold']
                }]
        }, '\n'
    ];

    var thead = [
        {
            text: 'No.',
            style: 'tableHeader'
        }, {
            text: 'Nama Barang',
            style: 'tableHeader'
        }, {
            text: 'Jumlah',
            style: 'tableHeader'
        }, {
            text: 'Harga Satuan',
            style: 'tableHeader'
        }, {
            text: 'Harga Total',
            style: 'tableHeader'
        }, {
            text: 'Nomor Order',
            style: 'tableHeader'
        }
    ];

    var tbody = items.map(function (item, index) {
        return [{
            text: (index + 1).toString() || '',
            style: ['size08', 'center']
        }, {
                text: item.product.name,
                style: ['size08', 'left']
            }, {
                text: `${item.quantity} ${item.uom.unit}`,
                style: ['size08', 'right']
            }, {
                columns: [{
                    width: '5%',
                    text: currency,
                    style: ['size08']
                }, {
                        width: '*',
                        text: parseFloat(item.pricePerUnit).toLocaleString(locale, locale.currency),
                        style: ['size08', 'right']
                    }]
            }, {
                columns: [{
                    width: '5%',
                    text: currency,
                    style: ['size08']
                }, {
                        width: '*',
                        text: parseFloat(item.priceTotal).toLocaleString(locale, locale.currency),
                        style: ['size08', 'right']
                    }]
            }, {
                text: item.prNo,
                style: ['size08', 'left']
            }];
    });

    tbody = tbody.length > 0 ? tbody : [
        [{
            text: "tidak ada barang",
            style: ['size08', 'center'],
            colSpan: 6
        }, "", "", "", "", ""]
    ];

    var table = [{
        table: {
            widths: ['5%', '35%', '15%', '15%', '15%', '15%'],
            headerRows: 1,
            body: [].concat([thead], tbody)
        }
    }];

    var initialValue = {
        priceTotal: 0
    };

    var sum = (items.length > 0 ? items : [initialValue])
        .map(item => item.priceTotal)
        .reduce(function (prev, curr, index, arr) {
            return prev + curr;
        }, 0);

    var totalIncomeTax = unitPaymentCorrection.unitPaymentOrder.incomeTaxNo.length > 0 ? sum * 0.1 : 0;

    var totalKoreksiDenganPPN = ['\n',
        {
            columns: [{
                width: '30%',
                text: 'Jumlah',
                style: ['size08']
            }, {
                    width: '5%',
                    text: currency,
                    style: ['size08']
                }, {
                    width: '65%',
                    text: parseFloat(sum).toLocaleString(locale, locale.currency),
                    style: ['size08', 'right']
                }],
            margin: [350, 0, 0, 0]
        }, {
            columns: [{
                width: '30%',
                text: 'PPN',
                style: ['size08']
            }, {
                    width: '5%',
                    text: currency,
                    style: ['size08']
                }, {
                    width: '65%',
                    text: parseFloat(totalIncomeTax).toLocaleString(locale, locale.currency),
                    style: ['size08', 'right']
                }],
            margin: [350, 0, 0, 0]
        }, {
            columns: [{
                width: '30%',
                text: 'Total',
                style: ['size08']
            }, {
                    width: '5%',
                    text: currency,
                    style: ['size08']
                }, {
                    width: '65%',
                    text: parseFloat(sum + totalIncomeTax).toLocaleString(locale, locale.currency),
                    style: ['size08', 'right', 'bold']
                }],
            margin: [350, 0, 0, 0]
        },
        '\n'];

    var totalKoreksiTanpaPPN = ['\n',
        {
            columns: [{
                width: '30%',
                text: 'Jumlah',
                style: ['size08']
            }, {
                    width: '5%',
                    text: currency,
                    style: ['size08']
                }, {
                    width: '65%',
                    text: parseFloat(sum).toLocaleString(locale, locale.currency),
                    style: ['size08', 'right']
                }],
            margin: [350, 0, 0, 0]
        }, '\n'];

    var total =  unitPaymentCorrection.unitPaymentOrder.useIncomeTax ? totalKoreksiDenganPPN : totalKoreksiTanpaPPN;

    var terbilang = unitPaymentCorrection.unitPaymentOrder.useIncomeTax ? {
        text: `Terbilang : ${say(sum + totalIncomeTax, unitPaymentCorrection.items.find(r => true).currency.description)}`,
        style: ['size09', 'bold']
    } : {
            text: `Terbilang : ${say(sum, unitPaymentCorrection.items.find(r => true).currency.description)}`,
            style: ['size09', 'bold']
        };

    var footer = ['\n',
        {
            columns: [
                {
                    width: '50%',
                    columns: [{
                        width: '35%',
                        text: 'Perjanjian Pembayaran',
                        style: ['size08']
                    }, {
                            width: '3%',
                            text: ':',
                            style: ['size08']
                        }, {
                            width: '*',
                            text: moment(unitPaymentCorrection.unitPaymentOrder.dueDate).format(locale.date.format),
                            style: ['size08']
                        }]
                }, {
                    width: '50%',
                    columns: [{
                        width: '35%',
                        text: '',
                        style: ['size08']
                    }, {
                            width: '3%',
                            text: '',
                            style: ['size08']
                        }, {
                            width: '*',
                            text: "",
                            style: ['size08']
                        }]
                }]
        }, {
            columns: [
                {
                    width: '50%',
                    columns: [{
                        width: '35%',
                        text: 'Nota',
                        style: ['size08']
                    }, {
                            width: '3%',
                            text: ':',
                            style: ['size08']
                        }, {
                            width: '*',
                            text: `${unitPaymentCorrection.unitPaymentOrder.no}`,
                            style: ['size08']
                        }]
                }, {
                    width: '50%',
                    columns: [{
                        width: '35%',
                        text: 'Barang Datang',
                        style: ['size08']
                    }, {
                            width: '3%',
                            text: ':',
                            style: ['size08']
                        }, {
                            width: '*',
                            text: `${moment(sjDate).format(locale.date.format)} `,
                            style: ['size08']
                        }]
                }]
        }, {
            columns: [{
                width: '50%',
                columns: [{
                    width: '35%',
                    text: 'Keterangan',
                    style: ['size08']
                }, {
                        width: '3%',
                        text: ':',
                        style: ['size08']
                    }, {
                        width: '*',
                        text: unitPaymentCorrection.remark,
                        style: ['size08']
                    }]
            }, {
                    width: '50%',
                    columns: [{
                        width: '35%',
                        text: 'Nomor Nota Retur',
                        style: ['size08']
                    }, {
                            width: '3%',
                            text: ':',
                            style: ['size08']
                        }, {
                            width: '*',
                            text: unitPaymentCorrection.returNoteNo,
                            style: ['size08']
                        }]
                }]
        }, '\n'];

    var signature = [{
        columns: [{
            width: '25%',
            stack: ['Diperiksa,', 'Verifikasi', '\n\n\n\n', '(                               )'],
            style: ['center']
        }, {
                width: '25%',
                stack: ['Mengetahui,', 'Pimpinan Bagian', '\n\n\n\n', '(                               )'],
                style: ['center']
            }, {
                width: '25%',
                stack: ['Tanda Terima,', 'Bagian Pembelian', '\n\n\n\n', '(                               )'],
                style: ['center']
            }, {
                width: '25%',
                stack: ['Dibuat Oleh,', ' ', '\n\n\n\n', `(  ${unitPaymentCorrection._createdBy}  )`],
                style: ['center']
            }],
        style: ['size08']
    }, '\n', {
            text: `Dicetak Oleh ${unitPaymentCorrection._createdBy}`,
            style: ['size08']
        }];

    var dd = {
        pageSize: 'A5',
        pageOrientation: 'landscape',
        pageMargins: 20,
        content: [].concat(header, table, total, terbilang, footer, signature),
        styles: {
            size06: {
                fontSize: 6
            },
            size07: {
                fontSize: 7
            },
            size08: {
                fontSize: 8
            },
            size09: {
                fontSize: 9
            },
            size10: {
                fontSize: 10
            },
            size15: {
                fontSize: 15
            },
            bold: {
                bold: true
            },
            center: {
                alignment: 'center'
            },
            left: {
                alignment: 'left'
            },
            right: {
                alignment: 'right'
            },
            justify: {
                alignment: 'justify'
            },
            tableHeader: {
                bold: true,
                fontSize: 8,
                color: 'black',
                alignment: 'center'
            }
        }
    }

    return dd;
}<commit_msg>update file pdf<commit_after>var say = require('../../utils/say');
var global = require('../../global');

module.exports = function (unitPaymentCorrection) {

    var items = unitPaymentCorrection.items.map((item) => {
        return {
            quantity: item.quantity,
            uom: item.uom,
            product: item.product,
            pricePerUnit: item.pricePerUnit,
            priceTotal: item.priceTotal,
            prNo: item.purchaseOrder.purchaseRequest.no
        };
    });

    items = [].concat.apply([], items);
    var iso = "FM-PB-00-06-015";
    var currency = unitPaymentCorrection.items.find(r => true).currency.code;
    var urDates = unitPaymentCorrection.unitPaymentOrder.items.map(unitPaymentOrderItem => {
        return new Date(unitPaymentOrderItem.unitReceiptNote.date)
    })
    var sjDate = Math.max.apply(null, urDates);

    var locale = global.config.locale;

    var moment = require('moment');
    moment.locale(locale.name);

    var numberLocaleOptions = {
        style: 'decimal',
        maximumFractionDigits: 4

    };
    var header = [
        {
            columns: [
                {
                    width: '40%',
                    text: 'PT DAN LIRIS',
                    style: ['size15', 'bold', 'left']
                }, {
                    width: '60%',
                    text: 'NOTA DEBET',
                    style: ['size15', 'bold', 'left']

                }]
        }, {
            columns: [
                {
                    width: '70%',
                    text: 'BANARAN, GROGOL, SUKOHARJO',
                    style: ['size08']
                }, {
                    width: '30%',
                    stack: [
                        {
                            text: iso,
                            style: ['size09', 'bold']
                        },
                        `SUKOHARJO, ${moment(unitPaymentCorrection.date).format(locale.date.format)}`,
                        `(${unitPaymentCorrection.unitPaymentOrder.supplier.code}) ${unitPaymentCorrection.unitPaymentOrder.supplier.name}`,
                        `${unitPaymentCorrection.unitPaymentOrder.supplier.address}`],
                    alignment: 'left',
                    style: ['size08']

                }]
        }, '\n', {
            columns: [
                {
                    stack: [{
                        columns: [{
                            width: '35%',
                            text: "Retur/Potongan"
                        }, {
                                width: '5%',
                                text: ":"
                            }, {
                                width: '*',
                                text: unitPaymentCorrection.unitPaymentOrder.category.name
                            }],
                        style: ['size08']
                    }, {
                            columns: [{
                                width: '35%',
                                text: "Untuk"
                            }, {
                                    width: '5%',
                                    text: ":"
                                }, {
                                    width: '*',
                                    text: unitPaymentCorrection.unitPaymentOrder.division.name
                                }],
                            style: ['size08']
                        }
                    ]
                }, {
                    width: '20%',
                    text: ''
                }, {
                    width: '30%',
                    text: `Nomor ${unitPaymentCorrection.no}`,
                    style: ['size09', 'left', 'bold']
                }]
        }, '\n'
    ];

    var thead = [
        {
            text: 'No.',
            style: 'tableHeader'
        }, {
            text: 'Nama Barang',
            style: 'tableHeader'
        }, {
            text: 'Jumlah',
            style: 'tableHeader'
        }, {
            text: 'Harga Satuan',
            style: 'tableHeader'
        }, {
            text: 'Harga Total',
            style: 'tableHeader'
        }, {
            text: 'Nomor Order',
            style: 'tableHeader'
        }
    ];

    var tbody = items.map(function (item, index) {
        return [{
            text: (index + 1).toString() || '',
            style: ['size08', 'center']
        }, {
                text: item.product.name,
                style: ['size08', 'left']
            }, {
                text: `${item.quantity} ${item.uom.unit}`,
                style: ['size08', 'right']
            }, {
                columns: [{
                    width: '5%',
                    text: currency,
                    style: ['size08']
                }, {
                        width: '*',
                        text: parseFloat(item.pricePerUnit).toLocaleString(locale, locale.currency),
                        style: ['size08', 'right']
                    }]
            }, {
                columns: [{
                    width: '5%',
                    text: currency,
                    style: ['size08']
                }, {
                        width: '*',
                        text: parseFloat(item.priceTotal).toLocaleString(locale, locale.currency),
                        style: ['size08', 'right']
                    }]
            }, {
                text: item.prNo,
                style: ['size08', 'left']
            }];
    });

    tbody = tbody.length > 0 ? tbody : [
        [{
            text: "tidak ada barang",
            style: ['size08', 'center'],
            colSpan: 6
        }, "", "", "", "", ""]
    ];

    var table = [{
        table: {
            widths: ['5%', '35%', '15%', '15%', '15%', '15%'],
            headerRows: 1,
            body: [].concat([thead], tbody)
        }
    }];

    var initialValue = {
        priceTotal: 0
    };

    var sum = (items.length > 0 ? items : [initialValue])
        .map(item => item.priceTotal)
        .reduce(function (prev, curr, index, arr) {
            return prev + curr;
        }, 0);

    var useIncomeTax = unitPaymentCorrection.unitPaymentOrder.incomeTaxNo.length > 0 ? sum * 0.1 : 0;

    var totalKoreksiJumlah = ['\n',
        {
            columns: [{
                width: '30%',
                text: 'Jumlah',
                style: ['size08']
            }, {
                    width: '5%',
                    text: currency,
                    style: ['size08']
                }, {
                    width: '65%',
                    text: parseFloat(sum).toLocaleString(locale, locale.currency),
                    style: ['size08', 'right']
                }],
            margin: [350, 0, 0, 0]
        }, {
            columns: [{
                width: '30%',
                text: 'PPN',
                style: ['size08']
            }, {
                    width: '5%',
                    text: currency,
                    style: ['size08']
                }, {
                    width: '65%',
                    text: parseFloat(useIncomeTax).toLocaleString(locale, locale.currency),
                    style: ['size08', 'right']
                }],
            margin: [350, 0, 0, 0]
        }, {
            columns: [{
                width: '30%',
                text: 'Total',
                style: ['size08']
            }, {
                    width: '5%',
                    text: currency,
                    style: ['size08']
                }, {
                    width: '65%',
                    text: parseFloat(sum + useIncomeTax).toLocaleString(locale, locale.currency),
                    style: ['size08', 'right', 'bold']
                }],
            margin: [350, 0, 0, 0]
        },
        '\n'];

    var totalKoreksiHarga = ['\n',
        {
            columns: [{
                width: '30%',
                text: 'Jumlah',
                style: ['size08']
            }, {
                    width: '5%',
                    text: currency,
                    style: ['size08']
                }, {
                    width: '65%',
                    text: parseFloat(sum).toLocaleString(locale, locale.currency),
                    style: ['size08', 'right']
                }],
            margin: [350, 0, 0, 0]
        }, '\n'];

    var total = unitPaymentCorrection.correctionType === "Jumlah" ? totalKoreksiJumlah : totalKoreksiHarga;

    var terbilang = unitPaymentCorrection.correctionType === "Jumlah" ? {
        text: `Terbilang : ${say(sum + useIncomeTax, unitPaymentCorrection.items.find(r => true).currency.description)}`,
        style: ['size09', 'bold']
    } : {
            text: `Terbilang : ${say(sum, unitPaymentCorrection.items.find(r => true).currency.description)}`,
            style: ['size09', 'bold']
        };

    var footer = ['\n',
        {
            columns: [
                {
                    width: '50%',
                    columns: [{
                        width: '35%',
                        text: 'Perjanjian Pembayaran',
                        style: ['size08']
                    }, {
                            width: '3%',
                            text: ':',
                            style: ['size08']
                        }, {
                            width: '*',
                            text: moment(unitPaymentCorrection.unitPaymentOrder.dueDate).format(locale.date.format),
                            style: ['size08']
                        }]
                }, {
                    width: '50%',
                    columns: [{
                        width: '35%',
                        text: '',
                        style: ['size08']
                    }, {
                            width: '3%',
                            text: '',
                            style: ['size08']
                        }, {
                            width: '*',
                            text: "",
                            style: ['size08']
                        }]
                }]
        }, {
            columns: [
                {
                    width: '50%',
                    columns: [{
                        width: '35%',
                        text: 'Nota',
                        style: ['size08']
                    }, {
                            width: '3%',
                            text: ':',
                            style: ['size08']
                        }, {
                            width: '*',
                            text: `${unitPaymentCorrection.unitPaymentOrder.no}`,
                            style: ['size08']
                        }]
                }, {
                    width: '50%',
                    columns: [{
                        width: '35%',
                        text: 'Barang Datang',
                        style: ['size08']
                    }, {
                            width: '3%',
                            text: ':',
                            style: ['size08']
                        }, {
                            width: '*',
                            text: `${moment(sjDate).format(locale.date.format)} `,
                            style: ['size08']
                        }]
                }]
        }, {
            columns: [{
                width: '50%',
                columns: [{
                    width: '35%',
                    text: 'Keterangan',
                    style: ['size08']
                }, {
                        width: '3%',
                        text: ':',
                        style: ['size08']
                    }, {
                        width: '*',
                        text: unitPaymentCorrection.remark,
                        style: ['size08']
                    }]
            }, {
                    width: '50%',
                    columns: [{
                        width: '35%',
                        text: 'Nomor Nota Retur',
                        style: ['size08']
                    }, {
                            width: '3%',
                            text: ':',
                            style: ['size08']
                        }, {
                            width: '*',
                            text: unitPaymentCorrection.returNoteNo,
                            style: ['size08']
                        }]
                }]
        }, '\n'];

    var signature = [{
        columns: [{
            width: '25%',
            stack: ['Diperiksa,', 'Verifikasi', '\n\n\n\n', '(                               )'],
            style: ['center']
        }, {
                width: '25%',
                stack: ['Mengetahui,', 'Pimpinan Bagian', '\n\n\n\n', '(                               )'],
                style: ['center']
            }, {
                width: '25%',
                stack: ['Tanda Terima,', 'Bagian Pembelian', '\n\n\n\n', '(                               )'],
                style: ['center']
            }, {
                width: '25%',
                stack: ['Dibuat Oleh,', ' ', '\n\n\n\n', `(  ${unitPaymentCorrection._createdBy}  )`],
                style: ['center']
            }],
        style: ['size08']
    }, '\n', {
            text: `Dicetak Oleh ${unitPaymentCorrection._createdBy}`,
            style: ['size08']
        }];

    var dd = {
        pageSize: 'A5',
        pageOrientation: 'landscape',
        pageMargins: 20,
        content: [].concat(header, table, total, terbilang, footer, signature),
        styles: {
            size06: {
                fontSize: 6
            },
            size07: {
                fontSize: 7
            },
            size08: {
                fontSize: 8
            },
            size09: {
                fontSize: 9
            },
            size10: {
                fontSize: 10
            },
            size15: {
                fontSize: 15
            },
            bold: {
                bold: true
            },
            center: {
                alignment: 'center'
            },
            left: {
                alignment: 'left'
            },
            right: {
                alignment: 'right'
            },
            justify: {
                alignment: 'justify'
            },
            tableHeader: {
                bold: true,
                fontSize: 8,
                color: 'black',
                alignment: 'center'
            }
        }
    }

    return dd;
}<|endoftext|>


<commit_before>
var Base = function(app) {
	this.setup(app);
};
Base.METATAGS_VARNAME = 'metaTags';
Base.prototype = {
	setup : function(app) {
		throw new Error('Please implement setup');
	},
	getMetaTags : function(res) {
		var metaTags = res.locals[Base.METATAGS_VARNAME];
		if(!metaTags) {
			metaTags = [];
			res.locals[Base.METATAGS_VARNAME] = metaTags;
		}
		return metaTags;
	},
	addMetaTag : function(res, meta) {
		var metaTags = this.getMetaTags(res);
		metaTags.push(meta);
		return this;
	},
	error404Page : function(req, res) {
		res.statusCode = 404;
		res.render('error/404.html', {
			layout : 'layouts/layout.html',
			result : null
		});
	}
};

module.exports = Base;
<commit_msg>Support mapping of http method and setup for express. Setup this.route for each subclass and define GET, POST, … for handling request.<commit_after>var methods = ['GET', 'POST', 'HEAD', 'PUT', 'DELETE', 'TRACE', 'CONNECT', 'OPTIONS', 'ALL'];

var Base = function(app) {
	this.route = null;
	this.setup(app);
	this.routeSetup(app);
};
Base.METATAGS_VARNAME = 'metaTags';
Base.prototype = {
	setup : function(app) {
		throw new Error('Please implement setup');
	},
	routeSetup: function(app) {
		var self = this, count = 0;
		if (self.route) {
			methods.forEach(function(method) {
				var lMethod = method.toLowerCase();
				if (typeof self[method] === 'function') {
					app[lMethod](self.route, self[method].bind(self));
					count++;
				} else if (typeof self[lMethod] == 'function') {
					app[lMethod](self.route, self[lMethod].bind(self));
					count++;
				}
			});
			if (count < 1) {
				console.log(self.route, 'specified as route but no match for any method: ', methods.join(', '));
			}
		}
	},
	getMetaTags : function(res) {
		var metaTags = res.locals[Base.METATAGS_VARNAME];
		if(!metaTags) {
			metaTags = [];
			res.locals[Base.METATAGS_VARNAME] = metaTags;
		}
		return metaTags;
	},
	addMetaTag : function(res, meta) {
		var metaTags = this.getMetaTags(res);
		metaTags.push(meta);
		return this;
	},
	error404Page : function(req, res) {
		res.statusCode = 404;
		res.render('error/404.html', {
			layout : 'layouts/layout.html',
			result : null
		});
	}
};

module.exports = Base;
<|endoftext|>


<commit_before>import React from 'react';

import { mount } from 'enzyme';

import moment from 'moment';

import * as keys from '../../src/keys';

import DayPickerInput, { HIDE_TIMEOUT } from '../../src/DayPickerInput';
import DayPicker from '../../src/DayPicker';

describe('DayPickerInput', () => {
  describe('events', () => {
    describe('click', () => {
      it('should show the overlay when the input is clicked', () => {
        const wrapper = mount(<DayPickerInput />);
        wrapper.find('input').simulate('click');
        expect(wrapper.find('.DayPicker')).toBeDefined();
      });
      it('should not show the overlay when the input is disabled', () => {
        const wrapper = mount(
          <DayPickerInput inputProps={{ disabled: true }} />
        );
        wrapper.find('input').simulate('click');
        // The following won't work until https://github.com/airbnb/enzyme/issues/386 is fixed.
        // expect(wrapper.find('.DayPicker')).not.toBeDefined();
      });
      it('should call `onClick` event handler', () => {
        const onClick = jest.fn();
        const wrapper = mount(<DayPickerInput inputProps={{ onClick }} />);
        wrapper.find('input').simulate('click');
        expect(onClick).toHaveBeenCalledTimes(1);
      });
    });

    describe('focus', () => {
      it('should show the overlay when the input is focused', () => {
        const wrapper = mount(<DayPickerInput />);
        wrapper.find('input').simulate('focus');
        expect(wrapper.find('.DayPicker')).toBeDefined();
      });
      it('should call `onFocus` event handler', () => {
        const onFocus = jest.fn();
        const wrapper = mount(<DayPickerInput inputProps={{ onFocus }} />);
        wrapper.find('input').simulate('focus');
        expect(onFocus).toHaveBeenCalledTimes(1);
      });
    });

    describe('overlayfocus', () => {
      afterEach(() => {
        document.activeElement.blur();
      });
      it('should focus the input if keepFocus is true', () => {
        const wrapper = mount(<DayPickerInput showOverlay keepFocus />);
        wrapper.find('.DayPickerInput-Overlay').simulate('focus');
        const instance = wrapper.instance();
        expect(document.activeElement).toEqual(instance.input);
      });
      it('should not focus the input if keepFocus is false', () => {
        const wrapper = mount(<DayPickerInput showOverlay keepFocus={false} />);
        wrapper.find('.DayPickerInput-Overlay').simulate('focus');
        const instance = wrapper.instance();
        expect(document.activeElement).not.toEqual(instance.input);
      });
    });
    describe('overlayblur', () => {
      it('should hide the overlay', done => {
        const wrapper = mount(<DayPickerInput showOverlay keepFocus />);
        wrapper.find('.DayPickerInput-Overlay').simulate('focus');
        wrapper.find('.DayPickerInput-Overlay').simulate('blur');
        setTimeout(() => {
          wrapper.update();
          expect(wrapper.instance().overlayHasFocus).toBeTruthy();
          done();
        });
      });
    });

    describe('blur', () => {
      it('should hide the overlay when the input is blurred', done => {
        const wrapper = mount(<DayPickerInput value="12/15/2017" />);
        wrapper.find('input').simulate('focus');
        wrapper.find('input').simulate('blur');
        setTimeout(() => {
          wrapper.update();
          expect(wrapper.find('.DayPicker')).toHaveLength(0);
          done();
        }, 100);
      });
      it('should call `onBlur` event handler', () => {
        const onBlur = jest.fn();
        const wrapper = mount(<DayPickerInput inputProps={{ onBlur }} />);
        wrapper.find('input').simulate('blur');
        expect(onBlur).toHaveBeenCalledTimes(1);
      });
    });
    describe('change', () => {
      it('should call `onChange` event handler', () => {
        const onChange = jest.fn();
        const wrapper = mount(<DayPickerInput inputProps={{ onChange }} />);
        wrapper.find('input').simulate('change');
        expect(onChange).toHaveBeenCalledTimes(1);
      });
      it('should update the input if its value is empty', () => {
        const wrapper = mount(<DayPickerInput />);
        wrapper.find('input').simulate('change', { target: { value: ' ' } });
        wrapper.update();
        expect(wrapper.find('input')).toHaveProp('value', ' ');
      });
      it("should call `onDayChange` if the input's value is empty", () => {
        const onDayChange = jest.fn();
        const wrapper = mount(<DayPickerInput onDayChange={onDayChange} />);
        const input = wrapper.find('input');
        input.simulate('change', { target: { value: '' } });
        expect(onDayChange).toHaveBeenCalledWith(undefined, {});
      });
      it("should update the input's value if the value is not a valid date", () => {
        const wrapper = mount(<DayPickerInput />);
        const input = wrapper.find('input');
        input.simulate('change', { target: { value: 'foo' } });
        expect(wrapper.find('input')).toHaveProp('value', 'foo');
      });
      it('should call `onDayChange` with `undefined` if the value is not a valid date', () => {
        const onDayChange = jest.fn();
        const wrapper = mount(<DayPickerInput onDayChange={onDayChange} />);
        const input = wrapper.find('input');
        input.simulate('change', { target: { value: 'foo' } });
        expect(onDayChange).toHaveBeenCalledWith(undefined, {});
      });
      it("should update the input's value and the displayed month", () => {
        const wrapper = mount(<DayPickerInput />);
        const input = wrapper.find('input');
        wrapper.instance().showDayPicker();
        wrapper.update();
        input.simulate('change', { target: { value: '2013-12-16' } });
        expect(wrapper.find('.DayPicker-Caption').first()).toHaveText(
          'December 2013'
        );
        input.simulate('change', { target: { value: '2015-10-11' } });
        expect(wrapper.find('.DayPicker-Caption').first()).toHaveText(
          'October 2015'
        );
      });
      it('should call `onDayChange` with modifiers', () => {
        const onDayChange = jest.fn();
        const testDay = new Date(2015, 11, 20);
        const wrapper = mount(
          <DayPickerInput
            dayPickerProps={{
              modifiers: { foo: testDay },
              selectedDays: testDay,
              disabledDays: testDay,
            }}
            onDayChange={onDayChange}
          />
        );
        const input = wrapper.find('input');
        wrapper.instance().showDayPicker();
        wrapper.update();
        input.simulate('change', { target: { value: '2015-12-20' } });
        expect(onDayChange).toHaveBeenCalledTimes(1);
        expect(onDayChange.mock.calls[0][0]).toEqual(new Date(2015, 11, 20));
        expect(onDayChange.mock.calls[0][1]).toEqual({
          foo: true,
          selected: true,
          disabled: true,
        });
      });
    });
    describe('keydown', () => {
      it('should hide the overlay on TAB', () => {
        const wrapper = mount(<DayPickerInput />);
        wrapper.instance().showDayPicker();
        wrapper.update();
        wrapper.find('input').simulate('keydown', { keyCode: keys.TAB });
        expect(wrapper.state('showOverlay')).toBe(false);
      });
      it('should call `onKeyDown` event handler', () => {
        const onKeyDown = jest.fn();
        const wrapper = mount(<DayPickerInput inputProps={{ onKeyDown }} />);
        wrapper.find('input').simulate('keydown');
        expect(onKeyDown).toHaveBeenCalledTimes(1);
      });
    });
    describe('keyup', () => {
      it('should hide the overlay on ESC', () => {
        const wrapper = mount(<DayPickerInput />);
        wrapper.instance().showDayPicker();
        wrapper.update();
        wrapper.find('input').simulate('keyup', { keyCode: keys.ESC });
        expect(wrapper.state('showOverlay')).toBe(false);
      });
      it('should call `onKeyUp` event handler', () => {
        const onKeyUp = jest.fn();
        const wrapper = mount(<DayPickerInput inputProps={{ onKeyUp }} />);
        wrapper.find('input').simulate('keyup');
        expect(onKeyUp).toHaveBeenCalledTimes(1);
      });
    });
    describe('dayclick', () => {
      it('should call `onDayClick` event handler', () => {
        const onDayClick = jest.fn();
        const wrapper = mount(
          <DayPickerInput dayPickerProps={{ onDayClick }} />
        );
        wrapper.instance().showDayPicker();
        wrapper.update();
        wrapper
          .find('.DayPicker-Day')
          .at(6)
          .simulate('click');
        expect(onDayClick).toHaveBeenCalledTimes(1);
      });
      it('should select and display the clicked day', () => {
        const wrapper = mount(
          <DayPickerInput dayPickerProps={{ month: new Date(2017, 1) }} />
        );
        wrapper.instance().showDayPicker();
        wrapper.update();
        wrapper
          .find('.DayPicker-Day')
          .at(10)
          .simulate('click');
        expect(wrapper.find('input')).toHaveProp('value', '2017-2-8');
        expect(wrapper.find('.DayPicker-Caption')).toHaveText('February 2017');
        expect(wrapper.find('.DayPicker-Day--selected')).toHaveText('8');
      });
      it('should call `onDayChange` when clicking on a day', () => {
        const onDayChange = jest.fn();
        const wrapper = mount(
          <DayPickerInput
            onDayChange={onDayChange}
            dayPickerProps={{
              month: new Date(2017, 1),
              modifiers: { foo: new Date(2017, 1, 8) },
            }}
          />
        );
        wrapper.instance().showDayPicker();
        wrapper.update();
        wrapper
          .find('.DayPicker-Day')
          .at(10)
          .simulate('click');
        expect(moment(onDayChange.mock.calls[0][0]).format('L')).toBe(
          '02/08/2017'
        );
        expect(onDayChange.mock.calls[0][1]).toEqual({ foo: true });
      });
      it('should hide the day picker when clicking on a day', done => {
        const wrapper = mount(<DayPickerInput />);
        wrapper.instance().showDayPicker();
        wrapper.update();
        wrapper
          .find('.DayPicker-Day')
          .at(10)
          .simulate('click');
        expect(wrapper.instance().hideTimeout).not.toBeNull();
        setTimeout(() => {
          wrapper.update();
          expect(wrapper.find(DayPicker)).toHaveLength(0);
          done();
        }, HIDE_TIMEOUT + 50);
      });
      it('should not hide the day picker if `hideOnDayClick` is false', () => {
        const wrapper = mount(<DayPickerInput hideOnDayClick={false} />);
        wrapper.instance().showDayPicker();
        wrapper.update();
        wrapper
          .find('.DayPicker-Day')
          .at(10)
          .simulate('click');
        expect(wrapper.instance().hideTimeout).toBeNull();
        expect(wrapper.find('.DayPicker')).toBeDefined();
      });
      it('should unselect the clicked day if already selected (clickUnselectsDay)', () => {
        const wrapper = mount(
          <DayPickerInput
            value="2017-11-8"
            clickUnselectsDay
            dayPickerProps={{
              month: new Date(2017, 10),
              selectedDays: new Date(2017, 10, 8),
            }}
          />
        );
        wrapper.instance().showDayPicker();
        wrapper.update();
        wrapper
          .find('.DayPicker-Day')
          .at(10)
          .simulate('click');
        expect(wrapper.find('input')).toHaveProp('value', '');
        expect(wrapper.find('.DayPicker-Day--selected')).toHaveLength(0);
      });
      it('should unselect the clicked day if already selected (clickUnselectsDay)', () => {
        const wrapper = mount(
          <DayPickerInput
            value="2017-11-8"
            clickUnselectsDay
            dayPickerProps={{
              month: new Date(2017, 10),
              selectedDays: [new Date(2017, 10, 8), new Date(2017, 10, 7)],
            }}
          />
        );
        wrapper.instance().showDayPicker();
        wrapper.update();
        wrapper
          .find('.DayPicker-Day')
          .at(10)
          .simulate('click');
        expect(wrapper.find('input')).toHaveProp('value', '');
        expect(wrapper.find('.DayPicker-Day--selected')).toHaveLength(1);
      });
      it('should call `onDayChange` when clicking a selected day', () => {
        const onDayChange = jest.fn();
        const wrapper = mount(
          <DayPickerInput
            value="2017-2-8"
            onDayChange={onDayChange}
            clickUnselectsDay
            dayPickerProps={{
              month: new Date(2017, 1),
              selectedDays: new Date(2017, 1, 8),
              modifiers: { foo: new Date(2017, 1, 8) },
            }}
          />
        );
        wrapper.instance().showDayPicker();
        wrapper.update();
        wrapper
          .find('.DayPicker-Day')
          .at(10)
          .simulate('click');
        expect(onDayChange).toHaveBeenCalledWith(undefined, {
          selected: true,
          foo: true,
        });
      });
      it('should not call `onDayChange` if the day is disabled', () => {
        const onDayChange = jest.fn();
        const wrapper = mount(
          <DayPickerInput
            value="2017-2-8"
            onDayChange={onDayChange}
            dayPickerProps={{
              month: new Date(2017, 1),
              disabledDays: new Date(2017, 1, 8),
            }}
          />
        );
        wrapper.instance().showDayPicker();
        wrapper.update();
        wrapper
          .find('.DayPicker-Day')
          .at(10)
          .simulate('click');
        expect(onDayChange).not.toHaveBeenCalled();
      });
      it('should use `dayPickerProps.selectedDays` after clicking a day', () => {
        const wrapper = mount(
          <DayPickerInput
            dayPickerProps={{
              month: new Date(2017, 1),
              selectedDays: [new Date(2017, 1, 8), new Date(2017, 1, 9)],
            }}
          />
        );
        wrapper.instance().showDayPicker();
        wrapper.update();
        wrapper
          .find('.DayPicker-Day')
          .at(9)
          .simulate('click');
        const selectedDays = wrapper.find('.DayPicker-Day--selected');
        expect(selectedDays).toHaveLength(2);
        expect(selectedDays.at(0)).toHaveText('8');
        expect(selectedDays.at(1)).toHaveText('9');
      });
      it('should use `dayPickerProps.selectedDays` after typing a valid day', () => {
        const wrapper = mount(
          <DayPickerInput
            dayPickerProps={{
              month: new Date(2017, 1),
              selectedDays: [new Date(2017, 1, 8), new Date(2017, 1, 9)],
            }}
          />
        );
        wrapper.instance().showDayPicker();
        wrapper.update();
        wrapper
          .find('input')
          .simulate('change', { target: { value: '02/07/2017' } });
        const selectedDays = wrapper.find('.DayPicker-Day--selected');
        expect(selectedDays).toHaveLength(2);
        expect(selectedDays.at(0)).toHaveText('8');
        expect(selectedDays.at(1)).toHaveText('9');
      });
    });

    describe('onMonthChange', () => {
      it('should update state when month changes', () => {
        const wrapper = mount(
          <DayPickerInput
            dayPickerProps={{
              initialMonth: new Date(2015, 7),
            }}
          />
        );
        const instance = wrapper.instance();
        instance.showDayPicker();
        wrapper.update();
        instance.getDayPicker().showNextMonth();
        expect(instance.state.month.getMonth()).toEqual(8);
      });

      it('should call onMonthChange when month changes', () => {
        const handleMonthChange = jest.fn();
        const wrapper = mount(
          <DayPickerInput
            dayPickerProps={{
              onMonthChange: handleMonthChange,
              initialMonth: new Date(2015, 7),
            }}
          />
        );
        const instance = wrapper.instance();
        instance.showDayPicker();
        wrapper.update();
        instance.getDayPicker().showNextMonth();
        expect(handleMonthChange).toHaveBeenCalled();
        expect(handleMonthChange.mock.calls[0][0].getMonth()).toEqual(8);
      });
    });
  });
});
<commit_msg>Add missing timeout<commit_after>import React from 'react';

import { mount } from 'enzyme';

import moment from 'moment';

import * as keys from '../../src/keys';

import DayPickerInput, { HIDE_TIMEOUT } from '../../src/DayPickerInput';
import DayPicker from '../../src/DayPicker';

describe('DayPickerInput', () => {
  describe('events', () => {
    describe('click', () => {
      it('should show the overlay when the input is clicked', () => {
        const wrapper = mount(<DayPickerInput />);
        wrapper.find('input').simulate('click');
        expect(wrapper.find('.DayPicker')).toBeDefined();
      });
      it('should not show the overlay when the input is disabled', () => {
        const wrapper = mount(
          <DayPickerInput inputProps={{ disabled: true }} />
        );
        wrapper.find('input').simulate('click');
        // The following won't work until https://github.com/airbnb/enzyme/issues/386 is fixed.
        // expect(wrapper.find('.DayPicker')).not.toBeDefined();
      });
      it('should call `onClick` event handler', () => {
        const onClick = jest.fn();
        const wrapper = mount(<DayPickerInput inputProps={{ onClick }} />);
        wrapper.find('input').simulate('click');
        expect(onClick).toHaveBeenCalledTimes(1);
      });
    });

    describe('focus', () => {
      it('should show the overlay when the input is focused', () => {
        const wrapper = mount(<DayPickerInput />);
        wrapper.find('input').simulate('focus');
        expect(wrapper.find('.DayPicker')).toBeDefined();
      });
      it('should call `onFocus` event handler', () => {
        const onFocus = jest.fn();
        const wrapper = mount(<DayPickerInput inputProps={{ onFocus }} />);
        wrapper.find('input').simulate('focus');
        expect(onFocus).toHaveBeenCalledTimes(1);
      });
    });

    describe('overlayfocus', () => {
      afterEach(() => {
        document.activeElement.blur();
      });
      it('should focus the input if keepFocus is true', () => {
        const wrapper = mount(<DayPickerInput showOverlay keepFocus />);
        wrapper.find('.DayPickerInput-Overlay').simulate('focus');
        const instance = wrapper.instance();
        expect(document.activeElement).toEqual(instance.input);
      });
      it('should not focus the input if keepFocus is false', () => {
        const wrapper = mount(<DayPickerInput showOverlay keepFocus={false} />);
        wrapper.find('.DayPickerInput-Overlay').simulate('focus');
        const instance = wrapper.instance();
        expect(document.activeElement).not.toEqual(instance.input);
      });
    });
    describe('overlayblur', () => {
      it('should hide the overlay', done => {
        const wrapper = mount(<DayPickerInput showOverlay keepFocus />);
        wrapper.find('.DayPickerInput-Overlay').simulate('focus');
        wrapper.find('.DayPickerInput-Overlay').simulate('blur');
        setTimeout(() => {
          wrapper.update();
          expect(wrapper.instance().overlayHasFocus).toBeTruthy();
          done();
        }, 100);
      });
    });

    describe('blur', () => {
      it('should hide the overlay when the input is blurred', done => {
        const wrapper = mount(<DayPickerInput value="12/15/2017" />);
        wrapper.find('input').simulate('focus');
        wrapper.find('input').simulate('blur');
        setTimeout(() => {
          wrapper.update();
          expect(wrapper.find('.DayPicker')).toHaveLength(0);
          done();
        }, 100);
      });
      it('should call `onBlur` event handler', () => {
        const onBlur = jest.fn();
        const wrapper = mount(<DayPickerInput inputProps={{ onBlur }} />);
        wrapper.find('input').simulate('blur');
        expect(onBlur).toHaveBeenCalledTimes(1);
      });
    });
    describe('change', () => {
      it('should call `onChange` event handler', () => {
        const onChange = jest.fn();
        const wrapper = mount(<DayPickerInput inputProps={{ onChange }} />);
        wrapper.find('input').simulate('change');
        expect(onChange).toHaveBeenCalledTimes(1);
      });
      it('should update the input if its value is empty', () => {
        const wrapper = mount(<DayPickerInput />);
        wrapper.find('input').simulate('change', { target: { value: ' ' } });
        wrapper.update();
        expect(wrapper.find('input')).toHaveProp('value', ' ');
      });
      it("should call `onDayChange` if the input's value is empty", () => {
        const onDayChange = jest.fn();
        const wrapper = mount(<DayPickerInput onDayChange={onDayChange} />);
        const input = wrapper.find('input');
        input.simulate('change', { target: { value: '' } });
        expect(onDayChange).toHaveBeenCalledWith(undefined, {});
      });
      it("should update the input's value if the value is not a valid date", () => {
        const wrapper = mount(<DayPickerInput />);
        const input = wrapper.find('input');
        input.simulate('change', { target: { value: 'foo' } });
        expect(wrapper.find('input')).toHaveProp('value', 'foo');
      });
      it('should call `onDayChange` with `undefined` if the value is not a valid date', () => {
        const onDayChange = jest.fn();
        const wrapper = mount(<DayPickerInput onDayChange={onDayChange} />);
        const input = wrapper.find('input');
        input.simulate('change', { target: { value: 'foo' } });
        expect(onDayChange).toHaveBeenCalledWith(undefined, {});
      });
      it("should update the input's value and the displayed month", () => {
        const wrapper = mount(<DayPickerInput />);
        const input = wrapper.find('input');
        wrapper.instance().showDayPicker();
        wrapper.update();
        input.simulate('change', { target: { value: '2013-12-16' } });
        expect(wrapper.find('.DayPicker-Caption').first()).toHaveText(
          'December 2013'
        );
        input.simulate('change', { target: { value: '2015-10-11' } });
        expect(wrapper.find('.DayPicker-Caption').first()).toHaveText(
          'October 2015'
        );
      });
      it('should call `onDayChange` with modifiers', () => {
        const onDayChange = jest.fn();
        const testDay = new Date(2015, 11, 20);
        const wrapper = mount(
          <DayPickerInput
            dayPickerProps={{
              modifiers: { foo: testDay },
              selectedDays: testDay,
              disabledDays: testDay,
            }}
            onDayChange={onDayChange}
          />
        );
        const input = wrapper.find('input');
        wrapper.instance().showDayPicker();
        wrapper.update();
        input.simulate('change', { target: { value: '2015-12-20' } });
        expect(onDayChange).toHaveBeenCalledTimes(1);
        expect(onDayChange.mock.calls[0][0]).toEqual(new Date(2015, 11, 20));
        expect(onDayChange.mock.calls[0][1]).toEqual({
          foo: true,
          selected: true,
          disabled: true,
        });
      });
    });
    describe('keydown', () => {
      it('should hide the overlay on TAB', () => {
        const wrapper = mount(<DayPickerInput />);
        wrapper.instance().showDayPicker();
        wrapper.update();
        wrapper.find('input').simulate('keydown', { keyCode: keys.TAB });
        expect(wrapper.state('showOverlay')).toBe(false);
      });
      it('should call `onKeyDown` event handler', () => {
        const onKeyDown = jest.fn();
        const wrapper = mount(<DayPickerInput inputProps={{ onKeyDown }} />);
        wrapper.find('input').simulate('keydown');
        expect(onKeyDown).toHaveBeenCalledTimes(1);
      });
    });
    describe('keyup', () => {
      it('should hide the overlay on ESC', () => {
        const wrapper = mount(<DayPickerInput />);
        wrapper.instance().showDayPicker();
        wrapper.update();
        wrapper.find('input').simulate('keyup', { keyCode: keys.ESC });
        expect(wrapper.state('showOverlay')).toBe(false);
      });
      it('should call `onKeyUp` event handler', () => {
        const onKeyUp = jest.fn();
        const wrapper = mount(<DayPickerInput inputProps={{ onKeyUp }} />);
        wrapper.find('input').simulate('keyup');
        expect(onKeyUp).toHaveBeenCalledTimes(1);
      });
    });
    describe('dayclick', () => {
      it('should call `onDayClick` event handler', () => {
        const onDayClick = jest.fn();
        const wrapper = mount(
          <DayPickerInput dayPickerProps={{ onDayClick }} />
        );
        wrapper.instance().showDayPicker();
        wrapper.update();
        wrapper
          .find('.DayPicker-Day')
          .at(6)
          .simulate('click');
        expect(onDayClick).toHaveBeenCalledTimes(1);
      });
      it('should select and display the clicked day', () => {
        const wrapper = mount(
          <DayPickerInput dayPickerProps={{ month: new Date(2017, 1) }} />
        );
        wrapper.instance().showDayPicker();
        wrapper.update();
        wrapper
          .find('.DayPicker-Day')
          .at(10)
          .simulate('click');
        expect(wrapper.find('input')).toHaveProp('value', '2017-2-8');
        expect(wrapper.find('.DayPicker-Caption')).toHaveText('February 2017');
        expect(wrapper.find('.DayPicker-Day--selected')).toHaveText('8');
      });
      it('should call `onDayChange` when clicking on a day', () => {
        const onDayChange = jest.fn();
        const wrapper = mount(
          <DayPickerInput
            onDayChange={onDayChange}
            dayPickerProps={{
              month: new Date(2017, 1),
              modifiers: { foo: new Date(2017, 1, 8) },
            }}
          />
        );
        wrapper.instance().showDayPicker();
        wrapper.update();
        wrapper
          .find('.DayPicker-Day')
          .at(10)
          .simulate('click');
        expect(moment(onDayChange.mock.calls[0][0]).format('L')).toBe(
          '02/08/2017'
        );
        expect(onDayChange.mock.calls[0][1]).toEqual({ foo: true });
      });
      it('should hide the day picker when clicking on a day', done => {
        const wrapper = mount(<DayPickerInput />);
        wrapper.instance().showDayPicker();
        wrapper.update();
        wrapper
          .find('.DayPicker-Day')
          .at(10)
          .simulate('click');
        expect(wrapper.instance().hideTimeout).not.toBeNull();
        setTimeout(() => {
          wrapper.update();
          expect(wrapper.find(DayPicker)).toHaveLength(0);
          done();
        }, HIDE_TIMEOUT + 50);
      });
      it('should not hide the day picker if `hideOnDayClick` is false', () => {
        const wrapper = mount(<DayPickerInput hideOnDayClick={false} />);
        wrapper.instance().showDayPicker();
        wrapper.update();
        wrapper
          .find('.DayPicker-Day')
          .at(10)
          .simulate('click');
        expect(wrapper.instance().hideTimeout).toBeNull();
        expect(wrapper.find('.DayPicker')).toBeDefined();
      });
      it('should unselect the clicked day if already selected (clickUnselectsDay)', () => {
        const wrapper = mount(
          <DayPickerInput
            value="2017-11-8"
            clickUnselectsDay
            dayPickerProps={{
              month: new Date(2017, 10),
              selectedDays: new Date(2017, 10, 8),
            }}
          />
        );
        wrapper.instance().showDayPicker();
        wrapper.update();
        wrapper
          .find('.DayPicker-Day')
          .at(10)
          .simulate('click');
        expect(wrapper.find('input')).toHaveProp('value', '');
        expect(wrapper.find('.DayPicker-Day--selected')).toHaveLength(0);
      });
      it('should unselect the clicked day if already selected (clickUnselectsDay)', () => {
        const wrapper = mount(
          <DayPickerInput
            value="2017-11-8"
            clickUnselectsDay
            dayPickerProps={{
              month: new Date(2017, 10),
              selectedDays: [new Date(2017, 10, 8), new Date(2017, 10, 7)],
            }}
          />
        );
        wrapper.instance().showDayPicker();
        wrapper.update();
        wrapper
          .find('.DayPicker-Day')
          .at(10)
          .simulate('click');
        expect(wrapper.find('input')).toHaveProp('value', '');
        expect(wrapper.find('.DayPicker-Day--selected')).toHaveLength(1);
      });
      it('should call `onDayChange` when clicking a selected day', () => {
        const onDayChange = jest.fn();
        const wrapper = mount(
          <DayPickerInput
            value="2017-2-8"
            onDayChange={onDayChange}
            clickUnselectsDay
            dayPickerProps={{
              month: new Date(2017, 1),
              selectedDays: new Date(2017, 1, 8),
              modifiers: { foo: new Date(2017, 1, 8) },
            }}
          />
        );
        wrapper.instance().showDayPicker();
        wrapper.update();
        wrapper
          .find('.DayPicker-Day')
          .at(10)
          .simulate('click');
        expect(onDayChange).toHaveBeenCalledWith(undefined, {
          selected: true,
          foo: true,
        });
      });
      it('should not call `onDayChange` if the day is disabled', () => {
        const onDayChange = jest.fn();
        const wrapper = mount(
          <DayPickerInput
            value="2017-2-8"
            onDayChange={onDayChange}
            dayPickerProps={{
              month: new Date(2017, 1),
              disabledDays: new Date(2017, 1, 8),
            }}
          />
        );
        wrapper.instance().showDayPicker();
        wrapper.update();
        wrapper
          .find('.DayPicker-Day')
          .at(10)
          .simulate('click');
        expect(onDayChange).not.toHaveBeenCalled();
      });
      it('should use `dayPickerProps.selectedDays` after clicking a day', () => {
        const wrapper = mount(
          <DayPickerInput
            dayPickerProps={{
              month: new Date(2017, 1),
              selectedDays: [new Date(2017, 1, 8), new Date(2017, 1, 9)],
            }}
          />
        );
        wrapper.instance().showDayPicker();
        wrapper.update();
        wrapper
          .find('.DayPicker-Day')
          .at(9)
          .simulate('click');
        const selectedDays = wrapper.find('.DayPicker-Day--selected');
        expect(selectedDays).toHaveLength(2);
        expect(selectedDays.at(0)).toHaveText('8');
        expect(selectedDays.at(1)).toHaveText('9');
      });
      it('should use `dayPickerProps.selectedDays` after typing a valid day', () => {
        const wrapper = mount(
          <DayPickerInput
            dayPickerProps={{
              month: new Date(2017, 1),
              selectedDays: [new Date(2017, 1, 8), new Date(2017, 1, 9)],
            }}
          />
        );
        wrapper.instance().showDayPicker();
        wrapper.update();
        wrapper
          .find('input')
          .simulate('change', { target: { value: '02/07/2017' } });
        const selectedDays = wrapper.find('.DayPicker-Day--selected');
        expect(selectedDays).toHaveLength(2);
        expect(selectedDays.at(0)).toHaveText('8');
        expect(selectedDays.at(1)).toHaveText('9');
      });
    });

    describe('onMonthChange', () => {
      it('should update state when month changes', () => {
        const wrapper = mount(
          <DayPickerInput
            dayPickerProps={{
              initialMonth: new Date(2015, 7),
            }}
          />
        );
        const instance = wrapper.instance();
        instance.showDayPicker();
        wrapper.update();
        instance.getDayPicker().showNextMonth();
        expect(instance.state.month.getMonth()).toEqual(8);
      });

      it('should call onMonthChange when month changes', () => {
        const handleMonthChange = jest.fn();
        const wrapper = mount(
          <DayPickerInput
            dayPickerProps={{
              onMonthChange: handleMonthChange,
              initialMonth: new Date(2015, 7),
            }}
          />
        );
        const instance = wrapper.instance();
        instance.showDayPicker();
        wrapper.update();
        instance.getDayPicker().showNextMonth();
        expect(handleMonthChange).toHaveBeenCalled();
        expect(handleMonthChange.mock.calls[0][0].getMonth()).toEqual(8);
      });
    });
  });
});
<|endoftext|>