<commit_before>var fs = require('fs'),
    _ = require('lodash'),
    expect = require('expect.js'),
    util = require('../../lib/util'),
    request = require('postman-request'),

    fixtures = require('../fixtures'),
    Cookie = require('../../lib/index.js').Cookie,
    Response = require('../../lib/index.js').Response,
    Header = require('../../lib/index.js').Header;

/* global describe, it */
describe('Response', function () {
    describe.only('isResponse', function () {
        it('must distinguish between collections and other objects', function () {
            var response = new Response(),
                nonResponse = {};

            expect(Response.isResponse(response)).to.be(true);
            expect(Response.isResponse(nonResponse)).to.be(false);
        });
    });

    describe('json representation', function () {
        it('must match what the response was initialized with', function () {
            var rawResponse = fixtures.collectionV2.item[0].response[0],
                response = new Response(rawResponse),
                jsonified = response.toJSON();
            expect(jsonified.status).to.eql(rawResponse.status);
            expect(jsonified.code).to.eql(rawResponse.code);
            expect(jsonified.body).to.eql(rawResponse.body);
            expect(Header.unparse(jsonified.header).trim()).to.eql(rawResponse.header.trim());
            // Skip cookie tests, because cookies are tested independently.
            expect(jsonified).to.have.property('cookie');
        });

        it('must retain Buffer nature on stream property after new Response(response.toJSON())', function () {
            var rawResponse = {
                    body: 'response body',
                    stream: new Buffer([114, 101, 115, 112, 111, 110, 115, 101, 32, 98, 111, 100, 121])
                },
                response = new Response(rawResponse),
                jsonified = response.toJSON(),
                reconstructedResponse = new Response(jsonified);
            expect(util.bufferOrArrayBufferToString(reconstructedResponse.stream)).to.eql(util.bufferOrArrayBufferToString(rawResponse.stream));
        });
        it('must infer the http response reason phrase from the status code', function () {
            var rawResponse = {
                    name: 'a sample response',
                    originalRequest: 'http://postman-echo.com/status/200',
                    code: 410,
                    body: 'response body'
                },
                response = new Response(rawResponse),
                jsonified = response.toJSON();
            expect(jsonified.status.toLowerCase()).to.eql('gone');
            expect(jsonified.code).to.eql(rawResponse.code);
            expect(jsonified.body).to.eql(rawResponse.body);

            // Skip cookie tests, because cookies are tested independently.
            expect(jsonified).to.have.property('cookie');
        });
    });

    describe('body', function () {
        it('should parse response stream as text', function () {
            expect((new Response({
                stream: new Buffer([0x62, 0x75, 0x66, 0x66, 0x65, 0x72])
            })).text()).to.be('buffer');
        });

        it('should parse response as JSON', function () {
            expect((new Response({
                body: '{ "hello": "world" }'
            })).json()).to.eql({
                hello: 'world'
            });
        });

        it('should strip BOM from response while parsing JSON', function () {
            expect((new Response({
                body: String.fromCharCode(0xFEFF) + '{ "hello": "world" }'
            })).json()).to.eql({
                hello: 'world'
            });

            expect((new Response({
                body: String.fromCharCode(0xEFBBBF) + '{ "hello": "world" }'
            })).json()).to.eql({
                hello: 'world'
            });
        });

        it('should throw friendly error while failing to parse json body', function () {
            var response = new Response({
                    body: '{ "hello: "world" }'
                }),
                json,
                error;

            try {
                json = response.json();
            }
            catch (e) {
                error = e;
            }

            expect(json).not.be.ok();
            expect(error).be.ok();
            expect(error.toString()).be(
                'JSONError: Unexpected token \'w\' at 1:12\n' +
                '{ "hello: "world" }\n' +
                '           ^'
            );
        });
    });

    describe('size', function () {
        it('must match the total size of the response', function () {
            var rawResponse1 = fixtures.responseData1,
                rawResponse2 = fixtures.responseData2,
                response1 = new Response(rawResponse1),
                response2 = new Response(rawResponse2),
                size1 = response1.size(),
                size2 = response2.size();
            expect(size1.body + size1.header).to.eql(rawResponse1.header.length + rawResponse1.body.length);
            expect(size2.body + size2.header).to.eql(rawResponse1.header.length + rawResponse1.body.length);
        });

        it('must match the content-length of the response if gzip encoded', function () {
            var rawResponse = {
                    code: 200,
                    body: 'gzipped content xyzxyzxyzxyzxyzxyz',
                    header: 'Content-Encoding: gzip\nContent-Length: 10'
                },
                response = new Response(rawResponse);
            expect(response.size().body).to.eql(10);
        });

        it('must match the content-length of the response if deflate encoded', function () {
            var rawResponse = {
                    code: 200,
                    body: 'gzipped content xyzxyzxyzxyzxyzxyz',
                    header: 'Content-Encoding: deflate\nContent-Length: 20'
                },
                response = new Response(rawResponse);
            expect(response.size().body).to.eql(20);
        });
    });

    // skip this test sub-suite in the browser
    ((typeof window === 'undefined') ? describe : describe.skip)('createFromNode', function () {
        var isNode4 = (/^v4\./).test(process.version),
            baseUrl = 'https://postman-echo.com',
            isHeader = Header.isHeader.bind(Header),
            isCookie = Cookie.isCookie.bind(Cookie),

            getBuffer = function (array) {
                return isNode4 ? new Buffer(array) : Buffer.from(new Uint32Array(array));
            },

            validateResponse = function (response) {
                var json = response.toJSON(),
                    buffer = getBuffer(json.stream.data);

                expect(json.code).to.be.a('number');
                expect(json.status).to.be.a('string');
                expect(json.responseSize).to.be.a('number');

                expect(json.stream).to.be.an('object');
                expect(json.stream.type).to.be('Buffer');
                expect(json.stream.data).to.be.an('array');
                expect(buffer.toString()).to.be(response.body);

                expect(json.header).to.be.an('array');
                expect(json.cookie).to.be.an('array');

                expect(_.every(response.headers.members, isHeader)).to.be(true);
                expect(_.every(response.cookies.members, isCookie)).to.be(true);
            };

        it('should correctly return a GET response', function (done) {
            request.get({
                url: baseUrl + '/get',
                encoding: null
            }, function (err, res) {
                if (err) {
                    return done(err);
                }

                var response = Response.createFromNode(res);
                validateResponse(response);
                done();
            });
        });

        describe('Should decode the response stream as text based on header charset', function () {
            it('charset(Shift_JIS) - Japanese', function () {
                expect((new Response({
                    header: [{
                        key: 'content-type',
                        value: 'text/html; charset=Shift_JIS'
                    }],
                    stream: fs.readFileSync('test/fixtures/japaneseCharacters.txt')
                })).text()).to.be('ハローポストマン\n'); // Harōposutoman
            });

            it('charset(windows-1251)- Cyrillic', function () {
                expect((new Response({
                    header: [{
                        key: 'content-type',
                        value: 'text/html; charset=windows-1251'
                    }],
                    stream: fs.readFileSync('test/fixtures/russianCharacters.txt')
                })).text()).to.be('Привет почтальон\n'); // Privet pochtal'on
            });

            it('Fallback to utf8, if it is not supported by iconvlite, say (ISO-8859-1)', function () {
                expect((new Response({
                    header: [{
                        key: 'content-type',
                        value: 'text/html; charset=ISO-8859-1'
                    }],
                    stream: new Buffer([0x62, 0x75, 0x66, 0x66, 0x65, 0x72])
                })).text()).to.be('buffer');
            });
        });

        describe('POST', function () {
            it('should correctly return a response for form-data', function (done) {
                var sampleArray = [1, 2, 3];

                request.post({
                    url: baseUrl + '/post',
                    encoding: null,
                    form: {
                        alpha: 'foo',
                        beta: 'bar',
                        buffer: getBuffer(sampleArray)
                    }
                }, function (err, res) {
                    if (err) {
                        return done(err);
                    }

                    var response = Response.createFromNode(res),
                        body = JSON.parse(response.toJSON().body);

                    expect(body.form.alpha).to.be('foo');
                    expect(body.form.beta).to.be('bar');
                    expect(body.form.buffer).to.be('\u0001\u0002\u0003');

                    validateResponse(response);
                    done();
                });
            });

            it('should correctly return a response for file uploads', function (done) {
                var file = fs.createReadStream('test/fixtures/icon.png'),
                    req = request.post({
                        url: baseUrl + '/post',
                        encoding: null
                    }, function (err, res) {
                        if (err) {
                            return done(err);
                        }

                        var response = Response.createFromNode(res),
                            body = JSON.parse(response.toJSON().body);

                        expect(body.files['icon.png']).to.match(/^data:application\/octet-stream;base64,/);

                        validateResponse(response);
                        done();
                    }),
                    form = req.form();

                form.append('file', file);
            });
        });

        it('should correctly return response headers', function (done) {
            request.get({
                url: baseUrl + '/response-headers?foo=bar&foo=bar2&bar=foo',
                encoding: null
            }, function (err, res) {
                if (err) {
                    return done(err);
                }

                var response = Response.createFromNode(res),
                    body = response.toJSON();

                expect(Header.headerValue(body.header, 'bar')).to.be('foo');
                expect(Header.headerValue(body.header, 'foo')).to.be('bar, bar2');

                validateResponse(response);
                done();
            });
        });

        // @todo: Supply cookie information to the createFromNode method to make these tests meaningful
        describe.skip('cookies', function () {
            var cookieUrl = baseUrl + '/cookies',
                stringify = function (cookies) {
                    return _.reduce(cookies, function (result, value, key) {
                        return result + key + '=' + value + ';';
                    }, '');
                };

            it('should correctly provide all cookies', function (done) {
                request.get({
                    url: cookieUrl,
                    jar: true,
                    encoding: null
                }, function (err, res, body) {
                    if (err) {
                        return done(err);
                    }

                    var cookieObject = JSON.parse(body).cookies,
                        stringifiedCookies = stringify(cookieObject),
                        response = Response.createFromNode(res, stringifiedCookies).toJSON();

                    expect(response.cookie).to.eql([]);
                    validateResponse(response);
                    done();
                });
            });

            it('should correctly set a cookie', function (done) {
                request.get({
                    url: cookieUrl + '/set?foo=bar',
                    jar: true,
                    encoding: null
                }, function (err, res, body) {
                    if (err) {
                        return done(err);
                    }

                    var cookieObject = JSON.parse(body).cookies,
                        stringifiedCookies = stringify(cookieObject),
                        response = Response.createFromNode(res, stringifiedCookies).toJSON();

                    expect(response.cookie).to.eql([{
                        key: 'foo',
                        hostOnly: true,
                        value: 'bar',
                        extensions: [{ key: '', value: true }]
                    }]);
                    validateResponse(response);
                    done();
                });
            });

            it('should correctly delete a previously set cookie', function (done) {
                request.get({
                    url: cookieUrl + '/delete?foo',
                    jar: true,
                    encoding: null
                }, function (err, res, body) {
                    if (err) {
                        return done(err);
                    }

                    var cookieObject = JSON.parse(body).cookies,
                        stringifiedCookies = stringify(cookieObject),
                        response = Response.createFromNode(res, stringifiedCookies).toJSON();

                    expect(response.cookie).to.eql([]);
                    validateResponse(response);
                    done();
                });
            });
        });

        describe('miscellaneous requests', function () {
            var checkMime = function (mime) {
                expect(mime.type).to.be('text');
                expect(mime.name).to.be('response');
                expect(mime.filename).to.be('response.' + mime.format);
                expect(mime._accuratelyDetected).to.be(true);
                expect(mime.source).to.be('header');
                expect(mime.detected).to.be(null);
            };

            it('should return a valid gzipped response', function (done) {
                request.get({
                    uri: baseUrl + '/gzip',
                    gzip: true,
                    encoding: null
                }, function (err, res) {
                    if (err) {
                        return done(err);
                    }

                    var response = Response.createFromNode(res),
                        json = response.toJSON(),
                        body = JSON.parse(json.body),
                        mime = response.mime();

                    expect(mime._originalContentType).to.be('application/json');
                    expect(mime._sanitisedContentType).to.be('application/json');

                    expect(body.gzipped).to.be(true);
                    expect(Header.headerValue(json.header, 'content-encoding')).to.be('gzip');

                    checkMime(mime);
                    validateResponse(response);
                    done();
                });
            });

            it('should return a valid deflated response', function (done) {
                request.get({
                    uri: baseUrl + '/deflate',
                    gzip: true,
                    encoding: null
                }, function (err, res) {
                    if (err) {
                        return done(err);
                    }

                    var response = Response.createFromNode(res),
                        body = JSON.parse(response.toJSON().body);

                    expect(body.deflated).to.be(true);

                    validateResponse(response);
                    done();
                });
            });

            it('should return a valid utf-8 encoded response', function (done) {
                request.get({
                    uri: baseUrl + '/encoding/utf8',
                    encoding: null
                }, function (err, res) {
                    if (err) {
                        return done(err);
                    }

                    var response = Response.createFromNode(res),
                        json = response.toJSON(),
                        mime = response.mime();

                    expect(mime._originalContentType).to.be('text/html; charset=utf-8');
                    expect(mime._sanitisedContentType).to.be('text/html');

                    expect(Header.headerValue(json.header, 'content-type')).to.match(/^text\/html/);
                    expect(json.body).to.match(/<html>.*/);

                    checkMime(mime);
                    validateResponse(response);
                    done();
                });
            });
        });
    });
});
<commit_msg>Removed test .only<commit_after>var fs = require('fs'),
    _ = require('lodash'),
    expect = require('expect.js'),
    util = require('../../lib/util'),
    request = require('postman-request'),

    fixtures = require('../fixtures'),
    Cookie = require('../../lib/index.js').Cookie,
    Response = require('../../lib/index.js').Response,
    Header = require('../../lib/index.js').Header;

/* global describe, it */
describe('Response', function () {
    describe('isResponse', function () {
        it('must distinguish between collections and other objects', function () {
            var response = new Response(),
                nonResponse = {};

            expect(Response.isResponse(response)).to.be(true);
            expect(Response.isResponse(nonResponse)).to.be(false);
        });
    });

    describe('json representation', function () {
        it('must match what the response was initialized with', function () {
            var rawResponse = fixtures.collectionV2.item[0].response[0],
                response = new Response(rawResponse),
                jsonified = response.toJSON();
            expect(jsonified.status).to.eql(rawResponse.status);
            expect(jsonified.code).to.eql(rawResponse.code);
            expect(jsonified.body).to.eql(rawResponse.body);
            expect(Header.unparse(jsonified.header).trim()).to.eql(rawResponse.header.trim());
            // Skip cookie tests, because cookies are tested independently.
            expect(jsonified).to.have.property('cookie');
        });

        it('must retain Buffer nature on stream property after new Response(response.toJSON())', function () {
            var rawResponse = {
                    body: 'response body',
                    stream: new Buffer([114, 101, 115, 112, 111, 110, 115, 101, 32, 98, 111, 100, 121])
                },
                response = new Response(rawResponse),
                jsonified = response.toJSON(),
                reconstructedResponse = new Response(jsonified);
            expect(util.bufferOrArrayBufferToString(reconstructedResponse.stream)).to.eql(util.bufferOrArrayBufferToString(rawResponse.stream));
        });
        it('must infer the http response reason phrase from the status code', function () {
            var rawResponse = {
                    name: 'a sample response',
                    originalRequest: 'http://postman-echo.com/status/200',
                    code: 410,
                    body: 'response body'
                },
                response = new Response(rawResponse),
                jsonified = response.toJSON();
            expect(jsonified.status.toLowerCase()).to.eql('gone');
            expect(jsonified.code).to.eql(rawResponse.code);
            expect(jsonified.body).to.eql(rawResponse.body);

            // Skip cookie tests, because cookies are tested independently.
            expect(jsonified).to.have.property('cookie');
        });
    });

    describe('body', function () {
        it('should parse response stream as text', function () {
            expect((new Response({
                stream: new Buffer([0x62, 0x75, 0x66, 0x66, 0x65, 0x72])
            })).text()).to.be('buffer');
        });

        it('should parse response as JSON', function () {
            expect((new Response({
                body: '{ "hello": "world" }'
            })).json()).to.eql({
                hello: 'world'
            });
        });

        it('should strip BOM from response while parsing JSON', function () {
            expect((new Response({
                body: String.fromCharCode(0xFEFF) + '{ "hello": "world" }'
            })).json()).to.eql({
                hello: 'world'
            });

            expect((new Response({
                body: String.fromCharCode(0xEFBBBF) + '{ "hello": "world" }'
            })).json()).to.eql({
                hello: 'world'
            });
        });

        it('should throw friendly error while failing to parse json body', function () {
            var response = new Response({
                    body: '{ "hello: "world" }'
                }),
                json,
                error;

            try {
                json = response.json();
            }
            catch (e) {
                error = e;
            }

            expect(json).not.be.ok();
            expect(error).be.ok();
            expect(error.toString()).be(
                'JSONError: Unexpected token \'w\' at 1:12\n' +
                '{ "hello: "world" }\n' +
                '           ^'
            );
        });
    });

    describe('size', function () {
        it('must match the total size of the response', function () {
            var rawResponse1 = fixtures.responseData1,
                rawResponse2 = fixtures.responseData2,
                response1 = new Response(rawResponse1),
                response2 = new Response(rawResponse2),
                size1 = response1.size(),
                size2 = response2.size();
            expect(size1.body + size1.header).to.eql(rawResponse1.header.length + rawResponse1.body.length);
            expect(size2.body + size2.header).to.eql(rawResponse1.header.length + rawResponse1.body.length);
        });

        it('must match the content-length of the response if gzip encoded', function () {
            var rawResponse = {
                    code: 200,
                    body: 'gzipped content xyzxyzxyzxyzxyzxyz',
                    header: 'Content-Encoding: gzip\nContent-Length: 10'
                },
                response = new Response(rawResponse);
            expect(response.size().body).to.eql(10);
        });

        it('must match the content-length of the response if deflate encoded', function () {
            var rawResponse = {
                    code: 200,
                    body: 'gzipped content xyzxyzxyzxyzxyzxyz',
                    header: 'Content-Encoding: deflate\nContent-Length: 20'
                },
                response = new Response(rawResponse);
            expect(response.size().body).to.eql(20);
        });
    });

    // skip this test sub-suite in the browser
    ((typeof window === 'undefined') ? describe : describe.skip)('createFromNode', function () {
        var isNode4 = (/^v4\./).test(process.version),
            baseUrl = 'https://postman-echo.com',
            isHeader = Header.isHeader.bind(Header),
            isCookie = Cookie.isCookie.bind(Cookie),

            getBuffer = function (array) {
                return isNode4 ? new Buffer(array) : Buffer.from(new Uint32Array(array));
            },

            validateResponse = function (response) {
                var json = response.toJSON(),
                    buffer = getBuffer(json.stream.data);

                expect(json.code).to.be.a('number');
                expect(json.status).to.be.a('string');
                expect(json.responseSize).to.be.a('number');

                expect(json.stream).to.be.an('object');
                expect(json.stream.type).to.be('Buffer');
                expect(json.stream.data).to.be.an('array');
                expect(buffer.toString()).to.be(response.body);

                expect(json.header).to.be.an('array');
                expect(json.cookie).to.be.an('array');

                expect(_.every(response.headers.members, isHeader)).to.be(true);
                expect(_.every(response.cookies.members, isCookie)).to.be(true);
            };

        it('should correctly return a GET response', function (done) {
            request.get({
                url: baseUrl + '/get',
                encoding: null
            }, function (err, res) {
                if (err) {
                    return done(err);
                }

                var response = Response.createFromNode(res);
                validateResponse(response);
                done();
            });
        });

        describe('Should decode the response stream as text based on header charset', function () {
            it('charset(Shift_JIS) - Japanese', function () {
                expect((new Response({
                    header: [{
                        key: 'content-type',
                        value: 'text/html; charset=Shift_JIS'
                    }],
                    stream: fs.readFileSync('test/fixtures/japaneseCharacters.txt')
                })).text()).to.be('ハローポストマン\n'); // Harōposutoman
            });

            it('charset(windows-1251)- Cyrillic', function () {
                expect((new Response({
                    header: [{
                        key: 'content-type',
                        value: 'text/html; charset=windows-1251'
                    }],
                    stream: fs.readFileSync('test/fixtures/russianCharacters.txt')
                })).text()).to.be('Привет почтальон\n'); // Privet pochtal'on
            });

            it('Fallback to utf8, if it is not supported by iconvlite, say (ISO-8859-1)', function () {
                expect((new Response({
                    header: [{
                        key: 'content-type',
                        value: 'text/html; charset=ISO-8859-1'
                    }],
                    stream: new Buffer([0x62, 0x75, 0x66, 0x66, 0x65, 0x72])
                })).text()).to.be('buffer');
            });
        });

        describe('POST', function () {
            it('should correctly return a response for form-data', function (done) {
                var sampleArray = [1, 2, 3];

                request.post({
                    url: baseUrl + '/post',
                    encoding: null,
                    form: {
                        alpha: 'foo',
                        beta: 'bar',
                        buffer: getBuffer(sampleArray)
                    }
                }, function (err, res) {
                    if (err) {
                        return done(err);
                    }

                    var response = Response.createFromNode(res),
                        body = JSON.parse(response.toJSON().body);

                    expect(body.form.alpha).to.be('foo');
                    expect(body.form.beta).to.be('bar');
                    expect(body.form.buffer).to.be('\u0001\u0002\u0003');

                    validateResponse(response);
                    done();
                });
            });

            it('should correctly return a response for file uploads', function (done) {
                var file = fs.createReadStream('test/fixtures/icon.png'),
                    req = request.post({
                        url: baseUrl + '/post',
                        encoding: null
                    }, function (err, res) {
                        if (err) {
                            return done(err);
                        }

                        var response = Response.createFromNode(res),
                            body = JSON.parse(response.toJSON().body);

                        expect(body.files['icon.png']).to.match(/^data:application\/octet-stream;base64,/);

                        validateResponse(response);
                        done();
                    }),
                    form = req.form();

                form.append('file', file);
            });
        });

        it('should correctly return response headers', function (done) {
            request.get({
                url: baseUrl + '/response-headers?foo=bar&foo=bar2&bar=foo',
                encoding: null
            }, function (err, res) {
                if (err) {
                    return done(err);
                }

                var response = Response.createFromNode(res),
                    body = response.toJSON();

                expect(Header.headerValue(body.header, 'bar')).to.be('foo');
                expect(Header.headerValue(body.header, 'foo')).to.be('bar, bar2');

                validateResponse(response);
                done();
            });
        });

        // @todo: Supply cookie information to the createFromNode method to make these tests meaningful
        describe.skip('cookies', function () {
            var cookieUrl = baseUrl + '/cookies',
                stringify = function (cookies) {
                    return _.reduce(cookies, function (result, value, key) {
                        return result + key + '=' + value + ';';
                    }, '');
                };

            it('should correctly provide all cookies', function (done) {
                request.get({
                    url: cookieUrl,
                    jar: true,
                    encoding: null
                }, function (err, res, body) {
                    if (err) {
                        return done(err);
                    }

                    var cookieObject = JSON.parse(body).cookies,
                        stringifiedCookies = stringify(cookieObject),
                        response = Response.createFromNode(res, stringifiedCookies).toJSON();

                    expect(response.cookie).to.eql([]);
                    validateResponse(response);
                    done();
                });
            });

            it('should correctly set a cookie', function (done) {
                request.get({
                    url: cookieUrl + '/set?foo=bar',
                    jar: true,
                    encoding: null
                }, function (err, res, body) {
                    if (err) {
                        return done(err);
                    }

                    var cookieObject = JSON.parse(body).cookies,
                        stringifiedCookies = stringify(cookieObject),
                        response = Response.createFromNode(res, stringifiedCookies).toJSON();

                    expect(response.cookie).to.eql([{
                        key: 'foo',
                        hostOnly: true,
                        value: 'bar',
                        extensions: [{ key: '', value: true }]
                    }]);
                    validateResponse(response);
                    done();
                });
            });

            it('should correctly delete a previously set cookie', function (done) {
                request.get({
                    url: cookieUrl + '/delete?foo',
                    jar: true,
                    encoding: null
                }, function (err, res, body) {
                    if (err) {
                        return done(err);
                    }

                    var cookieObject = JSON.parse(body).cookies,
                        stringifiedCookies = stringify(cookieObject),
                        response = Response.createFromNode(res, stringifiedCookies).toJSON();

                    expect(response.cookie).to.eql([]);
                    validateResponse(response);
                    done();
                });
            });
        });

        describe('miscellaneous requests', function () {
            var checkMime = function (mime) {
                expect(mime.type).to.be('text');
                expect(mime.name).to.be('response');
                expect(mime.filename).to.be('response.' + mime.format);
                expect(mime._accuratelyDetected).to.be(true);
                expect(mime.source).to.be('header');
                expect(mime.detected).to.be(null);
            };

            it('should return a valid gzipped response', function (done) {
                request.get({
                    uri: baseUrl + '/gzip',
                    gzip: true,
                    encoding: null
                }, function (err, res) {
                    if (err) {
                        return done(err);
                    }

                    var response = Response.createFromNode(res),
                        json = response.toJSON(),
                        body = JSON.parse(json.body),
                        mime = response.mime();

                    expect(mime._originalContentType).to.be('application/json');
                    expect(mime._sanitisedContentType).to.be('application/json');

                    expect(body.gzipped).to.be(true);
                    expect(Header.headerValue(json.header, 'content-encoding')).to.be('gzip');

                    checkMime(mime);
                    validateResponse(response);
                    done();
                });
            });

            it('should return a valid deflated response', function (done) {
                request.get({
                    uri: baseUrl + '/deflate',
                    gzip: true,
                    encoding: null
                }, function (err, res) {
                    if (err) {
                        return done(err);
                    }

                    var response = Response.createFromNode(res),
                        body = JSON.parse(response.toJSON().body);

                    expect(body.deflated).to.be(true);

                    validateResponse(response);
                    done();
                });
            });

            it('should return a valid utf-8 encoded response', function (done) {
                request.get({
                    uri: baseUrl + '/encoding/utf8',
                    encoding: null
                }, function (err, res) {
                    if (err) {
                        return done(err);
                    }

                    var response = Response.createFromNode(res),
                        json = response.toJSON(),
                        mime = response.mime();

                    expect(mime._originalContentType).to.be('text/html; charset=utf-8');
                    expect(mime._sanitisedContentType).to.be('text/html');

                    expect(Header.headerValue(json.header, 'content-type')).to.match(/^text\/html/);
                    expect(json.body).to.match(/<html>.*/);

                    checkMime(mime);
                    validateResponse(response);
                    done();
                });
            });
        });
    });
});
<|endoftext|>


<commit_before>;(function($B){

eval($B.InjectBuiltins())

var $ObjectDict = _b_.object.$dict

var $LocationDict = {__class__:$B.$type,__name__:'Location'}

$LocationDict.__mro__ = [$LocationDict,$ObjectDict]

function $Location(){ // used because of Firefox bug #814622
    var obj = {}
    for(var x in window.location){
        if(typeof window.location[x]==='function'){
            obj[x] = (function(f){
                return function(){
                    return f.apply(window.location,arguments)
                }
              })(window.location[x])
        }else{
            obj[x]=window.location[x]
        }
    }
    if(obj['replace']===undefined){ // IE
        obj['replace'] = function(url){window.location = url}
    }
    obj.__class__ = $LocationDict
    obj.toString = function(){return window.location.toString()}
    obj.__repr__ = obj.__str__ = obj.toString
    return obj
}

$LocationDict.$factory = $Location
$Location.$dict = $LocationDict

// Transforms a Javascript constructor into a Python function
// that returns instances of the constructor, converted to Python objects

var $JSConstructorDict = {__class__:$B.$type,__name__:'JSConstructor'}

$JSConstructorDict.__call__ = function(self){
    // self.js is a constructor
    // It takes Javascript arguments so we must convert
    // those passed to the Python function
    var args = [null]
    for(var i=1, _len_i = arguments.length; i < _len_i;i++){
        args.push(pyobj2jsobj(arguments[i]))
    }
    var factory = self.func.bind.apply(self.func, args)
    var res = new factory()
    // res is a Javascript object
    return $B.$JS2Py(res)
}

$JSConstructorDict.__mro__ = [$JSConstructorDict,$ObjectDict]

function JSConstructor(obj){
    //if(obj.__class__===$JSObjectDict){obj = obj.js}
    return {
        __class__:$JSConstructorDict,
        func:obj.js_func
    }
}
JSConstructor.__class__ = $B.$factory
JSConstructor.$dict = $JSConstructorDict
$JSConstructorDict.$factory = JSConstructor

// JSObject : wrapper around a native Javascript object

var jsobj2pyobj=$B.jsobj2pyobj=function(jsobj) {
    switch(jsobj) {
      case true:
      case false:
        return jsobj
    }

    if (Array.isArray(jsobj)) return _b_.list(jsobj)

    if (typeof jsobj === 'number') {
       if (jsobj.toString().indexOf('.') == -1) return _b_.int(jsobj)
       // for now, lets assume a float
       return _b_.float(jsobj)
    }

    return $B.JSObject(jsobj)
}

var pyobj2jsobj=$B.pyobj2jsobj=function(pyobj){
    // conversion of a Python object into a Javascript object
    if(pyobj===true || pyobj===false) return pyobj
    if(pyobj===_b_.None) return null

    var klass = $B.get_class(pyobj)
    if (klass === undefined) {
        // not a Python object , consider arg as Javascript object instead
        return pyobj;
    }
    if(klass===$JSObjectDict || klass===$JSConstructorDict){
        // Instances of JSObject and JSConstructor are transformed into the
        // underlying Javascript object

        // If the object is a function, the JSObject has a js_func attribute,
        // which is the original Javascript function
        if(pyobj.js_func!==undefined){return pyobj.js_func}
        return pyobj.js

    }else if(klass.__mro__.indexOf($B.DOMNodeDict)>-1){

        // instances of DOMNode or its subclasses are transformed into the
        // underlying DOM element
        return pyobj.elt

    }else if([_b_.list.$dict,_b_.tuple.$dict].indexOf(klass)>-1){

        // Python list : transform its elements
        var res = []
        for(var i=0, _len_i = pyobj.length; i < _len_i;i++){res.push(pyobj2jsobj(pyobj[i]))}
        return res

    }else if(klass===_b_.dict.$dict){

        // Python dictionaries are transformed into a Javascript object
        // whose attributes are the dictionary keys
        var jsobj = {}
        var items = _b_.list(_b_.dict.$dict.items(pyobj))
        for(var j=0, _len_j = items.length; j < _len_j;j++){
            jsobj[items[j][0]] = pyobj2jsobj(items[j][1])
        }
        return jsobj

    }else if(klass===$B.builtins.float.$dict){

        // Python floats are converted to the underlying value
        return pyobj.valueOf()

    }else if(klass===$B.$FunctionDict){
        // Transform arguments
        return function(){
            var args = []
            for(var i=0;i<arguments.length;i++){
                args.push(jsobj2pyobj(arguments[i]))
            }
            return pyobj.apply(null, args)
        }

    }else{
        // other types are left unchanged

        return pyobj

    }
}

var $JSObjectDict = {
    __class__:$B.$type,
    __name__:'JSObject',
    toString:function(){return '(JSObject)'}
}

$JSObjectDict.__bool__ = function(self){
    return (new Boolean(self.js)).valueOf()
}

$JSObjectDict.__dir__ = function(self){
    return Object.keys(self.js)
}

$JSObjectDict.__getattribute__ = function(self,attr){
    if(attr.substr(0,2)=='$$') attr=attr.substr(2)
    if(self.js===null) return $ObjectDict.__getattribute__(None,attr)
    if(attr==='__class__') return $JSObjectDict
    if(self.__class__===$JSObjectDict && attr=="$bind" &&
        self.js[attr]===undefined &&
        self.js['addEventListener']!==undefined){attr='addEventListener'}
    var js_attr = self.js[attr]
    if(self.js_func && self.js_func[attr]!==undefined){
        js_attr = self.js_func[attr]
    }

    if(js_attr !== undefined){
        if(typeof js_attr=='function'){
            // If the attribute of a JSObject is a function F, it is converted to a function G
            // where the arguments passed to the Python function G are converted to Javascript
            // objects usable by the underlying function F
            var res = function(){
                var args = [],arg
                for(var i=0, _len_i = arguments.length; i < _len_i;i++){
                    if(arguments[i].$nat!=undefined){
                        //
                        // Passing keyword arguments to a Javascript function
                        // raises a TypeError : since we don't know the
                        // signature of the function, the result of Brython
                        // code like foo(y=1, x=2) applied to a JS function
                        // defined by function foo(x, y) can't be determined.
                        //
                        throw TypeError("A Javascript function can't "+
                            "take keyword arguments")
                    }else{
                        args.push(pyobj2jsobj(arguments[i]))
                    }
                }
                // IE workaround
                if(attr === 'replace' && self.js === location) {
                    location.replace(args[0])
                    return
                }
                var res = js_attr.apply(self.js,args)
                if(typeof res == 'object') return JSObject(res)
                if(res===undefined) return None
                return $B.$JS2Py(res)
            }
            res.__repr__ = function(){return '<function '+attr+'>'}
            res.__str__ = function(){return '<function '+attr+'>'}
            return {__class__:$JSObjectDict,js:res,js_func:js_attr}
        }else{
            if(Array.isArray(self.js[attr])){return self.js[attr]}
            return $B.$JS2Py(self.js[attr])
        }
    }else if(self.js===window && attr==='$$location'){
        // special lookup because of Firefox bug
        // https://bugzilla.mozilla.org/show_bug.cgi?id=814622
        return $Location()
    }

    var res
    // search in classes hierarchy, following method resolution order
    var mro = self.__class__.__mro__
    for(var i=0, _len_i = mro.length; i < _len_i;i++){
        var v=mro[i][attr]
        if(v!==undefined){
            res = v
            break
        }
    }
    if(res!==undefined){
        if(typeof res==='function'){
            // res is the function in one of parent classes
            // return a function that takes self as first argument
            return function(){
                var args = [self],arg
                for(var i=0, _len_i = arguments.length; i < _len_i;i++){
                    arg = arguments[i]
                    if(arg && (arg.__class__===$JSObjectDict || arg.__class__===$JSConstructorDict)){
                        args.push(arg.js)
                    }else{
                        args.push(arg)
                    }
                }
                return res.apply(self,args)
            }
        }
        return $B.$JS2Py(res)
    }else{
        // XXX search __getattr__
        throw _b_.AttributeError("no attribute "+attr+' for '+self.js)
    }
}

$JSObjectDict.__getitem__ = function(self,rank){
    try{return getattr(self.js,'__getitem__')(rank)}
    catch(err){
        if(self.js[rank]!==undefined){return JSObject(self.js[rank])}
        throw _b_.KeyError(rank)
    }
}

var $JSObject_iterator = $B.$iterator_class('JS object iterator')
$JSObjectDict.__iter__ = function(self){
    if(window.Symbol && self.js[Symbol.iterator]!==undefined){
        // Javascript objects that support the iterable protocol, such as Map
        var items = []
        for(var item of self.js){items.push(jsobj2pyobj(item))}
        return $B.$iterator(items, $JSObject_iterator)
    }
    return $B.$iterator(self.js,$JSObject_iterator)
}

$JSObjectDict.__len__ = function(self){
    try{return getattr(self.js,'__len__')()}
    catch(err){
        throw _b_.AttributeError(self.js+' has no attribute __len__')
    }
}

$JSObjectDict.__mro__ = [$JSObjectDict,$ObjectDict]

$JSObjectDict.__repr__ = function(self){return "<JSObject wraps "+self.js+">"}

$JSObjectDict.__setattr__ = function(self,attr,value){
    if(isinstance(value,JSObject)){self.js[attr]=value.js}
    else{
        self.js[attr]=value
        if(typeof value=='function'){
            self.js[attr] = function(){
                var args = []
                for(var i=0, len=arguments.length;i<len;i++){
                    args.push(jsobj2pyobj(arguments[i]))
                }
                try{return value.apply(null, args)}
                catch(err){
                    err = $B.exception(err)
                    var info = _b_.getattr(err,'info')
                    throw Error(info+'\n'+err.__class__.__name__+
                        ': '+_b_.repr(err.args[0]))
                }
            }
        }
    }
}

$JSObjectDict.__setitem__ = $JSObjectDict.__setattr__

$JSObjectDict.__str__ = $JSObjectDict.__repr__

var no_dict = {'string':true,'function':true,'number':true,'boolean':true}

$JSObjectDict.bind = function(self, evt, func){
    return $JSObjectDict.__getattribute__(self, 'addEventListener').js(evt, func)
}

$JSObjectDict.to_dict = function(self){
    // Returns a Python dictionary based on the underlying Javascript object
    var res = _b_.dict()
    for(var key in self.js){
        var value = self.js[key]
        if(typeof value=='object' && !Array.isArray(value)){
            _b_.dict.$dict.__setitem__(res, key, $JSObjectDict.to_dict(JSObject(value)))
        }else{
            _b_.dict.$dict.__setitem__(res, key, value)
        }
    }
    return res
}

function JSObject(obj){
    if (obj === null) {return _b_.None}
    // If obj is a function, calling it with JSObject implies that it is
    // a function defined in Javascript. It must be wrapped in a JSObject
    // so that when called, the arguments are transformed into JS values
    if(typeof obj=='function'){return {__class__:$JSObjectDict,js:obj}}
    var klass = $B.get_class(obj)
    if(klass===_b_.list.$dict){
        // JS arrays not created by list() must be wrapped
        if(obj.__brython__) return obj
        return {__class__:$JSObjectDict,js:obj}
    }
    // we need to do this or nan is returned, when doing json.loads(...)
    if (klass === _b_.float.$dict) return _b_.float(obj)

    // If obj is a Python object, return it unchanged
    if(klass!==undefined) return obj
    return {__class__:$JSObjectDict,js:obj}  // wrap it
}
JSObject.__class__ = $B.$factory
JSObject.$dict = $JSObjectDict
$JSObjectDict.$factory = JSObject

$B.JSObject = JSObject
$B.JSConstructor = JSConstructor

})(__BRYTHON__)

<commit_msg>Add error trace for functions in js_objects.js<commit_after>;(function($B){

eval($B.InjectBuiltins())

var $ObjectDict = _b_.object.$dict

var $LocationDict = {__class__:$B.$type,__name__:'Location'}

$LocationDict.__mro__ = [$LocationDict,$ObjectDict]

function $Location(){ // used because of Firefox bug #814622
    var obj = {}
    for(var x in window.location){
        if(typeof window.location[x]==='function'){
            obj[x] = (function(f){
                return function(){
                    return f.apply(window.location,arguments)
                }
              })(window.location[x])
        }else{
            obj[x]=window.location[x]
        }
    }
    if(obj['replace']===undefined){ // IE
        obj['replace'] = function(url){window.location = url}
    }
    obj.__class__ = $LocationDict
    obj.toString = function(){return window.location.toString()}
    obj.__repr__ = obj.__str__ = obj.toString
    return obj
}

$LocationDict.$factory = $Location
$Location.$dict = $LocationDict

// Transforms a Javascript constructor into a Python function
// that returns instances of the constructor, converted to Python objects

var $JSConstructorDict = {__class__:$B.$type,__name__:'JSConstructor'}

$JSConstructorDict.__call__ = function(self){
    // self.js is a constructor
    // It takes Javascript arguments so we must convert
    // those passed to the Python function
    var args = [null]
    for(var i=1, _len_i = arguments.length; i < _len_i;i++){
        args.push(pyobj2jsobj(arguments[i]))
    }
    var factory = self.func.bind.apply(self.func, args)
    var res = new factory()
    // res is a Javascript object
    return $B.$JS2Py(res)
}

$JSConstructorDict.__mro__ = [$JSConstructorDict,$ObjectDict]

function JSConstructor(obj){
    //if(obj.__class__===$JSObjectDict){obj = obj.js}
    return {
        __class__:$JSConstructorDict,
        func:obj.js_func
    }
}
JSConstructor.__class__ = $B.$factory
JSConstructor.$dict = $JSConstructorDict
$JSConstructorDict.$factory = JSConstructor

// JSObject : wrapper around a native Javascript object

var jsobj2pyobj=$B.jsobj2pyobj=function(jsobj) {
    switch(jsobj) {
      case true:
      case false:
        return jsobj
    }

    if (Array.isArray(jsobj)) return _b_.list(jsobj)

    if (typeof jsobj === 'number') {
       if (jsobj.toString().indexOf('.') == -1) return _b_.int(jsobj)
       // for now, lets assume a float
       return _b_.float(jsobj)
    }

    return $B.JSObject(jsobj)
}

var pyobj2jsobj=$B.pyobj2jsobj=function(pyobj){
    // conversion of a Python object into a Javascript object
    if(pyobj===true || pyobj===false) return pyobj
    if(pyobj===_b_.None) return null

    var klass = $B.get_class(pyobj)
    if (klass === undefined) {
        // not a Python object , consider arg as Javascript object instead
        return pyobj;
    }
    if(klass===$JSObjectDict || klass===$JSConstructorDict){
        // Instances of JSObject and JSConstructor are transformed into the
        // underlying Javascript object

        // If the object is a function, the JSObject has a js_func attribute,
        // which is the original Javascript function
        if(pyobj.js_func!==undefined){return pyobj.js_func}
        return pyobj.js

    }else if(klass.__mro__.indexOf($B.DOMNodeDict)>-1){

        // instances of DOMNode or its subclasses are transformed into the
        // underlying DOM element
        return pyobj.elt

    }else if([_b_.list.$dict,_b_.tuple.$dict].indexOf(klass)>-1){

        // Python list : transform its elements
        var res = []
        for(var i=0, _len_i = pyobj.length; i < _len_i;i++){res.push(pyobj2jsobj(pyobj[i]))}
        return res

    }else if(klass===_b_.dict.$dict){

        // Python dictionaries are transformed into a Javascript object
        // whose attributes are the dictionary keys
        var jsobj = {}
        var items = _b_.list(_b_.dict.$dict.items(pyobj))
        for(var j=0, _len_j = items.length; j < _len_j;j++){
            jsobj[items[j][0]] = pyobj2jsobj(items[j][1])
        }
        return jsobj

    }else if(klass===$B.builtins.float.$dict){

        // Python floats are converted to the underlying value
        return pyobj.valueOf()

    }else if(klass===$B.$FunctionDict){
        // Transform arguments
        return function(){
            try{
                var args = []
                for(var i=0;i<arguments.length;i++){
                    args.push(jsobj2pyobj(arguments[i]))
                }
                return pyobj.apply(null, args)
            }catch(err){
                console.log(_b_.getattr(err,'info'))
                console.log(err.__name__+':', err.args[0])
                throw err
            }
        }

    }else{
        // other types are left unchanged

        return pyobj

    }
}

var $JSObjectDict = {
    __class__:$B.$type,
    __name__:'JSObject',
    toString:function(){return '(JSObject)'}
}

$JSObjectDict.__bool__ = function(self){
    return (new Boolean(self.js)).valueOf()
}

$JSObjectDict.__dir__ = function(self){
    return Object.keys(self.js)
}

$JSObjectDict.__getattribute__ = function(self,attr){
    if(attr.substr(0,2)=='$$') attr=attr.substr(2)
    if(self.js===null) return $ObjectDict.__getattribute__(None,attr)
    if(attr==='__class__') return $JSObjectDict
    if(self.__class__===$JSObjectDict && attr=="$bind" &&
        self.js[attr]===undefined &&
        self.js['addEventListener']!==undefined){attr='addEventListener'}
    var js_attr = self.js[attr]
    if(self.js_func && self.js_func[attr]!==undefined){
        js_attr = self.js_func[attr]
    }

    if(js_attr !== undefined){
        if(typeof js_attr=='function'){
            // If the attribute of a JSObject is a function F, it is converted to a function G
            // where the arguments passed to the Python function G are converted to Javascript
            // objects usable by the underlying function F
            var res = function(){
                var args = [],arg
                for(var i=0, _len_i = arguments.length; i < _len_i;i++){
                    if(arguments[i].$nat!=undefined){
                        //
                        // Passing keyword arguments to a Javascript function
                        // raises a TypeError : since we don't know the
                        // signature of the function, the result of Brython
                        // code like foo(y=1, x=2) applied to a JS function
                        // defined by function foo(x, y) can't be determined.
                        //
                        throw TypeError("A Javascript function can't "+
                            "take keyword arguments")
                    }else{
                        args.push(pyobj2jsobj(arguments[i]))
                    }
                }
                // IE workaround
                if(attr === 'replace' && self.js === location) {
                    location.replace(args[0])
                    return
                }
                var res = js_attr.apply(self.js,args)
                if(typeof res == 'object') return JSObject(res)
                if(res===undefined) return None
                return $B.$JS2Py(res)
            }
            res.__repr__ = function(){return '<function '+attr+'>'}
            res.__str__ = function(){return '<function '+attr+'>'}
            return {__class__:$JSObjectDict,js:res,js_func:js_attr}
        }else{
            if(Array.isArray(self.js[attr])){return self.js[attr]}
            return $B.$JS2Py(self.js[attr])
        }
    }else if(self.js===window && attr==='$$location'){
        // special lookup because of Firefox bug
        // https://bugzilla.mozilla.org/show_bug.cgi?id=814622
        return $Location()
    }

    var res
    // search in classes hierarchy, following method resolution order
    var mro = self.__class__.__mro__
    for(var i=0, _len_i = mro.length; i < _len_i;i++){
        var v=mro[i][attr]
        if(v!==undefined){
            res = v
            break
        }
    }
    if(res!==undefined){
        if(typeof res==='function'){
            // res is the function in one of parent classes
            // return a function that takes self as first argument
            return function(){
                var args = [self],arg
                for(var i=0, _len_i = arguments.length; i < _len_i;i++){
                    arg = arguments[i]
                    if(arg && (arg.__class__===$JSObjectDict || arg.__class__===$JSConstructorDict)){
                        args.push(arg.js)
                    }else{
                        args.push(arg)
                    }
                }
                return res.apply(self,args)
            }
        }
        return $B.$JS2Py(res)
    }else{
        // XXX search __getattr__
        throw _b_.AttributeError("no attribute "+attr+' for '+self.js)
    }
}

$JSObjectDict.__getitem__ = function(self,rank){
    try{return getattr(self.js,'__getitem__')(rank)}
    catch(err){
        if(self.js[rank]!==undefined){return JSObject(self.js[rank])}
        throw _b_.KeyError(rank)
    }
}

var $JSObject_iterator = $B.$iterator_class('JS object iterator')
$JSObjectDict.__iter__ = function(self){
    if(window.Symbol && self.js[Symbol.iterator]!==undefined){
        // Javascript objects that support the iterable protocol, such as Map
        var items = []
        for(var item of self.js){items.push(jsobj2pyobj(item))}
        return $B.$iterator(items, $JSObject_iterator)
    }
    return $B.$iterator(self.js,$JSObject_iterator)
}

$JSObjectDict.__len__ = function(self){
    try{return getattr(self.js,'__len__')()}
    catch(err){
        throw _b_.AttributeError(self.js+' has no attribute __len__')
    }
}

$JSObjectDict.__mro__ = [$JSObjectDict,$ObjectDict]

$JSObjectDict.__repr__ = function(self){return "<JSObject wraps "+self.js+">"}

$JSObjectDict.__setattr__ = function(self,attr,value){
    if(isinstance(value,JSObject)){self.js[attr]=value.js}
    else{
        self.js[attr]=value
        if(typeof value=='function'){
            self.js[attr] = function(){
                var args = []
                for(var i=0, len=arguments.length;i<len;i++){
                    args.push(jsobj2pyobj(arguments[i]))
                }
                try{return value.apply(null, args)}
                catch(err){
                    err = $B.exception(err)
                    var info = _b_.getattr(err,'info')
                    throw Error(info+'\n'+err.__class__.__name__+
                        ': '+_b_.repr(err.args[0]))
                }
            }
        }
    }
}

$JSObjectDict.__setitem__ = $JSObjectDict.__setattr__

$JSObjectDict.__str__ = $JSObjectDict.__repr__

var no_dict = {'string':true,'function':true,'number':true,'boolean':true}

$JSObjectDict.bind = function(self, evt, func){
    return $JSObjectDict.__getattribute__(self, 'addEventListener').js(evt, func)
}

$JSObjectDict.to_dict = function(self){
    // Returns a Python dictionary based on the underlying Javascript object
    var res = _b_.dict()
    for(var key in self.js){
        var value = self.js[key]
        if(typeof value=='object' && !Array.isArray(value)){
            _b_.dict.$dict.__setitem__(res, key, $JSObjectDict.to_dict(JSObject(value)))
        }else{
            _b_.dict.$dict.__setitem__(res, key, value)
        }
    }
    return res
}

function JSObject(obj){
    if (obj === null) {return _b_.None}
    // If obj is a function, calling it with JSObject implies that it is
    // a function defined in Javascript. It must be wrapped in a JSObject
    // so that when called, the arguments are transformed into JS values
    if(typeof obj=='function'){return {__class__:$JSObjectDict,js:obj}}
    var klass = $B.get_class(obj)
    if(klass===_b_.list.$dict){
        // JS arrays not created by list() must be wrapped
        if(obj.__brython__) return obj
        return {__class__:$JSObjectDict,js:obj}
    }
    // we need to do this or nan is returned, when doing json.loads(...)
    if (klass === _b_.float.$dict) return _b_.float(obj)

    // If obj is a Python object, return it unchanged
    if(klass!==undefined) return obj
    return {__class__:$JSObjectDict,js:obj}  // wrap it
}
JSObject.__class__ = $B.$factory
JSObject.$dict = $JSObjectDict
$JSObjectDict.$factory = JSObject

$B.JSObject = JSObject
$B.JSConstructor = JSConstructor

})(__BRYTHON__)

<|endoftext|>


<commit_before>// Copyright 2015 Google Inc. All Rights Reserved.
//
// 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.

// This library provides access to the Handler registration database.

"use strict";

(function() {

// Promise-based wrappers around indexedDB.

// Takes an IDBRequest object (the result of calling some indexedDB function).
// Returns a promise that resolves with the request's result, or rejects with
// the request's DOMError.
function promisify(request) {
  return new Promise((resolve, reject) => {
    request.onerror = e => reject(request.error);
    request.onsuccess = e => resolve(request.result);
  });
}

// Opens an indexeddb database. |upgradefunction| is called with the IDBDatabase
// as an argument, if an upgrade is needed (it should return a promise). Returns
// a promise that resolves with the IDBDatabase object.
function openDatabase(name, version, upgradefunction) {
  var request = window.indexedDB.open(name, version);
  return new Promise((resolve, reject) => {
    request.onerror = e => reject(request.error);
    request.onblocked = e => reject(
        new DOMError('The database cannot be upgraded because it is in use.'));
    request.onsuccess = e => resolve(request.result);
    request.onupgradeneeded = e => {
      upgradefunction(request.result)
          .then(unused => resolve(request.result), error => reject(error));
    }
  });
}

// Takes an IDBTransaction object and waits for the transaction to complete.
// Returns a promise that resolves (with no argument) when the transaction
// completes.
function transactionWait(transaction) {
  return new Promise((resolve, reject) => {
    transaction.onerror = e => reject(transaction.error);
    transaction.oncomplete = e => resolve();
  });
}

function storeAdd(objectStore, value, key) {
  return promisify(objectStore.add(value, key));
}

function storePut(objectStore, value, key) {
  return promisify(objectStore.put(value, key));
}

function storeDelete(objectStore, key) {
  return promisify(objectStore.delete(key));
}

function storeGet(objectStore, key) {
  return promisify(objectStore.get(key));
}

// Handler registration database code.

function onUpgradeNeeded(db) {
  // The 'handlers' store maps arbitrary ints onto Handler objects.
  var objectStore = db.createObjectStore('handlers', {autoIncrement: true});
  return new Promise((resolve, reject) => resolve());
}

function openRegistryDatabase() {
  return openDatabase('TestRegistry', 1, onUpgradeNeeded);
}

// Adds a new handler to the database.
function addHandler(db, handler) {
  var transaction = db.transaction(['handlers'], 'readwrite');
  var store = transaction.objectStore('handlers');
  storeAdd(store, handler);
  return transactionWait(transaction);
}

function getAllHandlers(db) {
  var transaction = db.transaction(['handlers']);
  var store = transaction.objectStore('handlers');
  var handlers = [];
  return new Promise((resolve, reject) => {
    store.openCursor().onsuccess = e => {
      var cursor = e.target.result;
      if (cursor) {
        handlers.push(cursor.value);
        cursor.continue();
      } else {
        resolve(handlers);
      }
    };
  });
}

})();
<commit_msg>Use the url as a key instead of an autoIncrement.<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
//
// 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.

// This library provides access to the Handler registration database.

"use strict";

(function() {

// Promise-based wrappers around indexedDB.

// Takes an IDBRequest object (the result of calling some indexedDB function).
// Returns a promise that resolves with the request's result, or rejects with
// the request's DOMError.
function promisify(request) {
  return new Promise((resolve, reject) => {
    request.onerror = e => reject(request.error);
    request.onsuccess = e => resolve(request.result);
  });
}

// Opens an indexeddb database. |upgradefunction| is called with the IDBDatabase
// and old version as arguments, if an upgrade is needed (it should return a
// promise). Returns a promise that resolves with the IDBDatabase object.
function openDatabase(name, version, upgradefunction) {
  var request = window.indexedDB.open(name, version);
  return new Promise((resolve, reject) => {
    request.onerror = e => reject(request.error);
    request.onblocked = e => reject(
        new DOMError('The database cannot be upgraded because it is in use.'));
    request.onsuccess = e => resolve(request.result);
    request.onupgradeneeded = e => {
      upgradefunction(request.result, e.oldVersion)
          .then(unused => resolve(request.result), error => reject(error));
    }
  });
}

// Takes an IDBTransaction object and waits for the transaction to complete.
// Returns a promise that resolves (with no argument) when the transaction
// completes.
function transactionWait(transaction) {
  return new Promise((resolve, reject) => {
    transaction.onerror = e => reject(transaction.error);
    transaction.oncomplete = e => resolve();
  });
}

function storeAdd(objectStore, value, key) {
  return promisify(objectStore.add(value, key));
}

function storePut(objectStore, value, key) {
  return promisify(objectStore.put(value, key));
}

function storeDelete(objectStore, key) {
  return promisify(objectStore.delete(key));
}

function storeGet(objectStore, key) {
  return promisify(objectStore.get(key));
}

// Handler registration database code.

function onUpgradeNeeded(db, oldVersion) {
  // Migration code.
  if (oldVersion < 2)
    db.deleteObjectStore('handlers');

  // The 'handlers' store maps arbitrary ints onto Handler objects.
  var objectStore = db.createObjectStore('handlers', {keyPath: 'url'});
  return new Promise((resolve, reject) => resolve());
}

function openRegistryDatabase() {
  return openDatabase('TestRegistry', 2, onUpgradeNeeded);
}

// Adds a new handler to the database.
function addHandler(db, handler) {
  var transaction = db.transaction(['handlers'], 'readwrite');
  var store = transaction.objectStore('handlers');
  storeAdd(store, handler);
  return transactionWait(transaction);
}

function getAllHandlers(db) {
  var transaction = db.transaction(['handlers']);
  var store = transaction.objectStore('handlers');
  var handlers = [];
  return new Promise((resolve, reject) => {
    store.openCursor().onsuccess = e => {
      var cursor = e.target.result;
      if (cursor) {
        handlers.push(cursor.value);
        cursor.continue();
      } else {
        resolve(handlers);
      }
    };
  });
}

})();
<|endoftext|>


<commit_before>/**
 * Mobile menu
 *
 * @since Riiskit 1.0.0
 */

jQuery(function ($) {

    "use strict";

	// Cache vars
	var body = $('body');

	// Other vars
	var menuBreakpoint = 649; // Use the same breakpoint as defined in CSS
	var active	= false;

	var nav		= $('.site-header__nav');
	var menu	= $('.menu-primary');
	var button	= $('.toggle-menu-btn');

    if ( ! nav || ! button ) {
        return;
    }
    if ( ! menu || ! menu.children().length ) {
        button.addClass('hide');
        return;
    }

    // Toggle
    if ( body.hasClass('mobile-menu-type__toggle') ) {

        (function () {
           button.on('click.riiskit', function() {
                menu.toggleClass('active');

                if ( active === false ) {
	                active = true;

		            $(this).attr('aria-pressed', 'true');
		        } else {
			        active = false;

			        $(this).attr('aria-pressed', 'false');
		        }
            } );

            // Make the menu visible again if it's is closed while
            // the window is resized past the breakpoint
            $.selector_cache(window).on('resize.riiskit', function() {
                if ( $.selector_cache(window).width() >= menuBreakpoint ) {
                    menu.addClass('show');
                } else {
                    if ( ! menu.hasClass('active') ) {
                        menu.removeClass('show');
                    }
                }
            });
        } ());
    } // end toggle

    // Slideout with sidr.js
    if ( body.hasClass('mobile-menu-type__slideout') && jQuery().sidr ) {

        (function () {
	        var sidrIsOpen = false;
	        var sidrSelector = 'rk-slideout';

			menu.attr('id', 'rk-slideout');

			// Config
            button.sidr({
                name: sidrSelector,
                side: 'right',
                onOpen: function(){
                    sidrIsOpen = true;
                    button.attr('aria-pressed', 'true');
                },
                onClose: function(){
                    sidrIsOpen = false;
                    button.attr('aria-pressed', 'false');
                },
                displace: false,
            });

            // Closing sidr

            // clicked outside sidr
            $.selector_cache('.site-header, .site-main, .site-footer').on('click.riiskit', function() {
                $.sidr('close', sidrSelector);

                button.attr('aria-pressed', 'false');
            });

            // resized window past the breakpoint
            $.selector_cache(window).on('resize.riiskit', function() {
                if( $.selector_cache(window).width() >= menuBreakpoint ) {
                    if ( sidrIsOpen ) {
                    	$.sidr('close', sidrSelector);

                    	button.attr('aria-pressed', 'false');
                    }

                    menu.addClass('show');
                } else {
                    menu.removeClass('show');
                }
            });
        } ());
    } // end slideout

}); // end jQuery document ready
<commit_msg>Fix closing sidr.js menu by clicking outside menu<commit_after>/**
 * Mobile menu
 *
 * @since Riiskit 1.0.0
 */

jQuery(function ($) {

    "use strict";

	// Cache vars
	var body = $('body');

	// Other vars
	var menuBreakpoint = 649; // Use the same breakpoint as defined in CSS
	var active	= false;

	var nav		= $('.site-header__nav');
	var menu	= $('.menu-primary');
	var button	= $('.toggle-menu-btn');

    if ( ! nav || ! button ) {
        return;
    }
    if ( ! menu || ! menu.children().length ) {
        button.addClass('hide');
        return;
    }

    // Toggle
    if ( body.hasClass('mobile-menu-type__toggle') ) {

        (function () {
           button.on('click.riiskit', function() {
                menu.toggleClass('active');

                if ( active === false ) {
	                active = true;

		            $(this).attr('aria-pressed', 'true');
		        } else {
			        active = false;

			        $(this).attr('aria-pressed', 'false');
		        }
            } );

            // Make the menu visible again if it's is closed while
            // the window is resized past the breakpoint
            $.selector_cache(window).on('resize.riiskit', function() {
                if ( $.selector_cache(window).width() >= menuBreakpoint ) {
                    menu.addClass('show');
                } else {
                    if ( ! menu.hasClass('active') ) {
                        menu.removeClass('show');
                    }
                }
            });
        } ());
    } // end toggle

    // Slideout with sidr.js
    if ( body.hasClass('mobile-menu-type__slideout') && jQuery().sidr ) {

        (function () {
	        var sidrIsOpen = false;
	        var sidrSelector = 'rk-slideout';

			menu.attr('id', 'rk-slideout');

            // Config, and adding / removing click outside listener for closing
            button.sidr({
                name: sidrSelector,
                side: 'right',
                onOpen: function(){
                    sidrIsOpen = true;
                    button.attr('aria-pressed', 'true');
                    // Add listener for clicks outside the menu when open
                    $.selector_cache('.site-header, .site-main, .site-footer').on('click.riiskit', function() {
                        $.sidr('close', sidrSelector);
                        button.attr('aria-pressed', 'false');
                    });
                },
                onClose: function(){
                    sidrIsOpen = false;
                    button.attr('aria-pressed', 'false');
                     $.selector_cache('.site-header, .site-main, .site-footer').off('click.riiskit');
                },
                displace: false,
            });

            // resized window past the breakpoint
            $.selector_cache(window).on('resize.riiskit', function() {
                if( $.selector_cache(window).width() >= menuBreakpoint ) {
                    if ( sidrIsOpen ) {
                    	$.sidr('close', sidrSelector);

                    	button.attr('aria-pressed', 'false');
                    }

                    menu.addClass('show');
                } else {
                    menu.removeClass('show');
                }
            });
        } ());
    } // end slideout

}); // end jQuery document ready
<|endoftext|>


<commit_before>import Head from 'next/head'
import Link from 'next/link'
import Router from 'next/router'
import Car from '../svg/car.svg'

export default () =>

      <title>My page title</title>
      <meta name='viewport' content='initial-scale=1.0, width=device-width' />

Hello world!

      Click  Router.push('/about')}>here to read more

    <Car />

<commit_msg>simplify the main index page<commit_after>import Head from 'next/head'
import Link from 'next/link'
import Router from 'next/router'
import MasterLayout from '../layouts/master'
import styled from 'react-emotion'

const SiteContainer = styled('div')`
  text-align: center;
  max-width: 500px;
  margin: 0 auto;
`

export default () => (
  <MasterLayout>
    <SiteContainer>
    </SiteContainer>
  </MasterLayout>
)
<|endoftext|>


<commit_before>var expect = require('expect.js'),
    Variable = require('../../lib/index.js').Variable;

/* global describe, it */
describe('Variable', function () {
    it('should initialise variable of type `any` and value `undefined`', function () {
        var v = new Variable();

        expect(v.value).to.be(undefined);
        expect(v.type).to.be('any');
    });

    it('should initialize variable with correct system value', function () {
        var v = new Variable();
        expect(v.system).to.be(undefined);

        v = new Variable({
            system: true
        });
        expect(v.system).to.be(true);

        v = new Variable({
            system: false
        });
        expect(v.system).to.be(false);
    });

    it('should update the sytem property of a variable', function () {
        var v = new Variable();
        v.update({ system: true });
        expect(v.system).to.be(true);

        v = new Variable({
            system: true
        });
        v.update({ system: false });
        expect(v.system).to.be(false);
    });

    it('should prepopulate value and type when passed to the constructor (string)', function () {
        var v = new Variable({
            value: 'Picard',
            type: 'string'
        });

        expect(v.value).to.be('Picard');
        expect(v.type).to.be('string');
    });

    it('should prepopulate value and type when passed to the constructor (number)', function () {
        var v = new Variable({
            value: 42,
            type: 'number'
        });

        expect(v.value).to.be(42);
        expect(v.type).to.be('number');
    });

    it('should prepopulate value and type when passed to the constructor (boolean)', function () {
        var v = new Variable({
            value: true,
            type: 'boolean'
        });

        expect(v.value).to.be(true);
        expect(v.type).to.be('boolean');
    });

    it('should prepopulate value and type when passed to the constructor (json)', function () {
        var vValue = { foo: 'bar' },
            v = new Variable({
                value: vValue,
                type: 'json'
            });

        expect(v.value).to.be(JSON.stringify(vValue));
        expect(v.type).to.be('json');
    });

    it('should typecast value during construction when type is provided (number)', function () {
        var v = new Variable({
            value: '108',
            type: 'number'
        });

        expect(v.value).to.be(108);
    });

    it('should typecast value during construction when type is provided (string)', function () {
        var v = new Variable({
            value: true,
            type: 'string'
        });

        expect(v.value).to.be('true');
    });

    it('should typecast value during construction when type is provided (boolean)', function () {
        var v = new Variable({
            value: 'foo',
            type: 'boolean'
        });

        expect(v.value).to.be(true);
    });

    it('should typecast value during construction when type is provided (json)', function () {
        var v = new Variable({
                value: null,
                type: 'json'
            }),
            v1 = new Variable({
                value: '{"foo":"bar"}',
                type: 'json'
            });

        expect(v.value).to.be('null');
        expect(v1.value).to.be('{"foo":"bar"}');
    });

    it('should support any data type if type is set to `any`', function () {
        var v = new Variable(),
            jsonValue = { iam: 'json' };

        v.set('Picard');
        expect(v.get()).to.be('Picard');

        v.set(3.142);
        expect(v.get()).to.be(3.142);

        v.set(jsonValue);
        expect(v.get()).to.be(jsonValue);
    });

    it('should type cast values to the specific type set', function () {
        var v = new Variable({
            type: 'string'
        });

        v.set('Picard');
        expect(v.get()).to.be('Picard');

        v.set(3.142);
        expect(v.get()).to.be('3.142');

        v.set({ foo: 'bar' });
        expect(v.get()).to.be('[object Object]');
    });

    it('should recast values when type is changed', function () {
        var v = new Variable({
            type: 'string'
        });

        v.set(3.142);
        expect(v.get()).to.be('3.142');

        v.valueType('number');
        expect(v.get()).to.be(3.142);
    });

    it('should recast values when type is changed (json)', function () {
        var v = new Variable({
            type: 'string'
        });

        v.set({ foo: 'bar' });
        expect(v.get()).to.be('[object Object]');

        v.valueType('json');
        expect(v.get()).to.be(null);
    });

    it('should handle functions correctly', function () {
        var v = new Variable({ type: 'string', key: 'foo', value: function () { return 'bar'; } });

        expect(v.get()).to.be('bar');
    });

    it('should not touch value functions when type is changed', function () {
        var v = new Variable({ type: 'string', key: 'foo', value: function () { return 'foo'; } });

        expect(v.get()).to.be('foo');

        v.valueType('boolean');

        expect(v.value).to.be.a('function');
        expect(v.get()).to.be(true);
    });

    it('should typecast returns of values as functions correctly', function () {
        var v = new Variable({ type: 'number', key: 'foo', value: function () { return '3.14'; } }),
            v2 = new Variable({ type: 'boolean', key: 'foo', value: function () { return 'bar'; } });

        // number
        expect(v.get()).to.be(3.14);

        // boolean
        expect(v2.get()).to.be(true);
    });

    describe('.set', function () {
        it('should handle functions correctly', function () {
            var variable = new Variable({ type: 'string' });

            expect(variable.set.bind(variable)).withArgs(function () {
                return 'random';
            }).to.not.throwError();
        });
    });

    describe('.valueOf', function () {
        it('should handle set arguments correctly', function () {
            var variable = new Variable({ type: 'string', key: 'foo', value: 'bar' });

            variable.valueOf('newVal');
            expect(variable.value).to.be('newVal');
        });
    });

    describe('.toString', function () {
        it('should convert nil values to empty string', function () {
            var variable;

            // no value
            variable = new Variable();
            expect(variable.toString()).to.be('');

            // value undefined
            variable = new Variable({ key: 'foo', value: undefined });
            expect(variable.toString()).to.be('');

            // value null
            variable = new Variable({ key: 'foo', value: null });
            expect(variable.toString()).to.be('');
        });

        it('should preserve empty string', function () {
            var variable;

            variable = new Variable({ key: 'foo', value: '' });
            expect(variable.toString()).to.be('');
        });

        it('should stringify false as "false"', function () {
            var variable;

            variable = new Variable({ key: 'foo', value: false });
            expect(variable.toString()).to.be('false');
        });

        it('should stringify 0 as "0"', function () {
            var variable;

            variable = new Variable({ key: 'foo', value: 0 });
            expect(variable.toString()).to.be('0');
        });

        it('should stringify NaN as "NaN"', function () {
            var variable;

            variable = new Variable({ key: 'foo', value: NaN });
            expect(variable.toString()).to.be('NaN');
        });

        it('should handle the absence of .valueOf correctly', function () {
            var variable = new Variable({ key: 'foo', value: Object.create(null) });

            expect(variable.toString()).to.be('');
        });
    });

    describe('.cast', function () {
        it('should use a default type of any if there is no existing type', function () {
            var variable = new Variable({ key: 'foo', value: 'bar' });

            delete variable.type;
            expect(variable.cast(123)).to.be.a('number');
        });
    });

    describe('.valueType', function () {
        it('should use the current type if no type is provided', function () {
            var variable = new Variable({ type: 'string', key: 'foo', value: 'bar' });

            expect(variable.valueType()).to.be('string');
        });

        it('should use the default type if no type is provided, and no type exists', function () {
            var variable = new Variable({ type: 'string', key: 'foo', value: 'bar' });
            delete variable.type;

            expect(variable.valueType()).to.be('any');
        });

        it('should cast to the specified type when specified', function () {
            var variable = new Variable({ type: 'string', key: 'foo', value: '123' });

            expect(variable.valueType('number')).to.be('number');
        });

        it('should handle invalid types correctly', function () {
            var variable = new Variable({ type: 'string', key: 'foo', value: '123' });
            delete variable.type;

            expect(variable.valueType('random')).to.be('any');
        });
    });

    describe('sanity', function () {
        it('constructor must be exported', function () {
            expect(Variable).to.be.a('function');
        });

        it('should create a new instance', function () {
            var v = new Variable();
            expect(v instanceof Variable).to.be.ok();
        });

        it('should not update if a non-object parameter is provided', function () {
            var v = new Variable({ key: 'foo', value: 'bar' });

            v.update();
            expect(v.key).to.be('foo');
            expect(v.value).to.be('bar');
        });
    });

    describe('isVariable', function () {
        it('should return true for a valid Variable instance', function () {
            expect(Variable.isVariable(new Variable({ type: 'string', key: 'foo', value: 'bar' }))).to.be(true);
        });

        it('should return false for a raw variable object', function () {
            expect(Variable.isVariable({ type: 'string', key: 'foo', value: 'bar' })).to.be(false);
        });

        it('should return false when called without arguments', function () {
            expect(Variable.isVariable()).to.be(false);
        });
    });
});
<commit_msg>Update variable tests to chai<commit_after>var expect = require('chai').expect,
    Variable = require('../../lib/index.js').Variable;

describe('Variable', function () {
    it('should initialise variable of type `any` and value `undefined`', function () {
        var v = new Variable();

        expect(v).to.deep.include({
            value: undefined,
            type: 'any'
        });
    });

    it('should initialize variable with correct system value', function () {
        var v = new Variable();
        expect(v.system).to.be.undefined;

        v = new Variable({
            system: true
        });
        expect(v.system).to.be.true;

        v = new Variable({
            system: false
        });
        expect(v.system).to.be.false;
    });

    it('should update the sytem property of a variable', function () {
        var v = new Variable();
        v.update({ system: true });
        expect(v.system).to.be.true;

        v = new Variable({
            system: true
        });
        v.update({ system: false });
        expect(v.system).to.be.false;
    });

    it('should prepopulate value and type when passed to the constructor (string)', function () {
        var v = new Variable({
            value: 'Picard',
            type: 'string'
        });

        expect(v).to.deep.include({
            value: 'Picard',
            type: 'string'
        });
    });

    it('should prepopulate value and type when passed to the constructor (number)', function () {
        var v = new Variable({
            value: 42,
            type: 'number'
        });

        expect(v).to.deep.include({
            value: 42,
            type: 'number'
        });
    });

    it('should prepopulate value and type when passed to the constructor (boolean)', function () {
        var v = new Variable({
            value: true,
            type: 'boolean'
        });

        expect(v).to.deep.include({
            value: true,
            type: 'boolean'
        });
    });

    it('should prepopulate value and type when passed to the constructor (json)', function () {
        var vValue = { foo: 'bar' },
            v = new Variable({
                value: vValue,
                type: 'json'
            });

        expect(v).to.deep.include({
            value: JSON.stringify(vValue),
            type: 'json'
        });
    });

    it('should typecast value during construction when type is provided (number)', function () {
        var v = new Variable({
            value: '108',
            type: 'number'
        });

        expect(v.value).to.equal(108);
    });

    it('should typecast value during construction when type is provided (string)', function () {
        var v = new Variable({
            value: true,
            type: 'string'
        });

        expect(v.value).to.equal('true');
    });

    it('should typecast value during construction when type is provided (boolean)', function () {
        var v = new Variable({
            value: 'foo',
            type: 'boolean'
        });

        expect(v.value).to.be.true;
    });

    it('should typecast value during construction when type is provided (json)', function () {
        var v = new Variable({
                value: null,
                type: 'json'
            }),
            v1 = new Variable({
                value: '{"foo":"bar"}',
                type: 'json'
            });

        expect(v.value).to.equal('null');
        expect(v1.value).to.equal('{"foo":"bar"}');

    });

    it('should support any data type if type is set to `any`', function () {
        var v = new Variable(),
            jsonValue = { iam: 'json' };

        v.set('Picard');
        expect(v.get()).to.equal('Picard');

        v.set(3.142);
        expect(v.get()).to.equal(3.142);

        v.set(jsonValue);
        expect(v.get()).to.equal(jsonValue);
    });

    it('should type cast values to the specific type set', function () {
        var v = new Variable({
            type: 'string'
        });

        v.set('Picard');
        expect(v.get()).to.equal('Picard');

        v.set(3.142);
        expect(v.get()).to.equal('3.142');

        v.set({ foo: 'bar' });
        expect(v.get()).to.equal('[object Object]');
    });

    it('should recast values when type is changed', function () {
        var v = new Variable({
            type: 'string'
        });

        v.set(3.142);
        expect(v.get()).to.equal('3.142');

        v.valueType('number');
        expect(v.get()).to.equal(3.142);
    });

    it('should recast values when type is changed (json)', function () {
        var v = new Variable({
            type: 'string'
        });

        v.set({ foo: 'bar' });
        expect(v.get()).to.equal('[object Object]');

        v.valueType('json');
        expect(v.get()).to.be.null;
    });

    it('should handle functions correctly', function () {
        var v = new Variable({ type: 'string', key: 'foo', value: function () { return 'bar'; } });

        expect(v.get()).to.equal('bar');
    });

    it('should not touch value functions when type is changed', function () {
        var v = new Variable({ type: 'string', key: 'foo', value: function () { return 'foo'; } });

        expect(v.get()).to.equal('foo');

        v.valueType('boolean');

        expect(v.value).to.be.a('function');
        expect(v.get()).to.be.true;
    });

    it('should typecast returns of values as functions correctly', function () {
        var v = new Variable({ type: 'number', key: 'foo', value: function () { return '3.14'; } }),
            v2 = new Variable({ type: 'boolean', key: 'foo', value: function () { return 'bar'; } });

        // number
        expect(v.get()).to.equal(3.14);

        // boolean
        expect(v2.get()).to.be.true;
    });

    describe('.set', function () {
        it('should handle functions correctly', function () {
            var variable = new Variable({ type: 'string' });

            expect(function () {
                variable.set.bind(variable)(function () {
                    return 'random';
                });
            }).to.not.throw();
        });
    });

    describe('.valueOf', function () {
        it('should handle set arguments correctly', function () {
            var variable = new Variable({ type: 'string', key: 'foo', value: 'bar' });

            variable.valueOf('newVal');
            expect(variable.value).to.equal('newVal');
        });
    });

    describe('.toString', function () {
        it('should convert nil values to empty string', function () {
            var variable;

            // no value
            variable = new Variable();
            expect(variable.toString()).to.equal('');

            // value undefined
            variable = new Variable({ key: 'foo', value: undefined });
            expect(variable.toString()).to.equal('');

            // value null
            variable = new Variable({ key: 'foo', value: null });
            expect(variable.toString()).to.equal('');
        });

        it('should preserve empty string', function () {
            var variable;

            variable = new Variable({ key: 'foo', value: '' });
            expect(variable.toString()).to.equal('');
        });

        it('should stringify false as "false"', function () {
            var variable;

            variable = new Variable({ key: 'foo', value: false });
            expect(variable.toString()).to.equal('false');
        });

        it('should stringify 0 as "0"', function () {
            var variable;

            variable = new Variable({ key: 'foo', value: 0 });
            expect(variable.toString()).to.equal('0');
        });

        it('should stringify NaN as "NaN"', function () {
            var variable;

            variable = new Variable({ key: 'foo', value: NaN });
            expect(variable.toString()).to.equal('NaN');
        });

        it('should handle the absence of .valueOf correctly', function () {
            var variable = new Variable({ key: 'foo', value: Object.create(null) });

            expect(variable.toString()).to.equal('');
        });
    });

    describe('.cast', function () {
        it('should use a default type of any if there is no existing type', function () {
            var variable = new Variable({ key: 'foo', value: 'bar' });

            delete variable.type;
            expect(variable.cast(123)).to.be.a('number');
        });
    });

    describe('.valueType', function () {
        it('should use the current type if no type is provided', function () {
            var variable = new Variable({ type: 'string', key: 'foo', value: 'bar' });

            expect(variable.valueType()).to.equal('string');
        });

        it('should use the default type if no type is provided, and no type exists', function () {
            var variable = new Variable({ type: 'string', key: 'foo', value: 'bar' });
            delete variable.type;

            expect(variable.valueType()).to.equal('any');
        });

        it('should cast to the specified type when specified', function () {
            var variable = new Variable({ type: 'string', key: 'foo', value: '123' });

            expect(variable.valueType('number')).to.equal('number');
        });

        it('should handle invalid types correctly', function () {
            var variable = new Variable({ type: 'string', key: 'foo', value: '123' });
            delete variable.type;

            expect(variable.valueType('random')).to.equal('any');
        });
    });

    describe('sanity', function () {
        it('constructor must be exported', function () {
            expect(Variable).to.be.a('function');
        });

        it('should create a new instance', function () {
            var v = new Variable();
            expect(v instanceof Variable).to.be.ok;
        });

        it('should not update if a non-object parameter is provided', function () {
            var v = new Variable({ key: 'foo', value: 'bar' });

            v.update();
            expect(v.key).to.equal('foo');
            expect(v.value).to.equal('bar');
        });
    });

    describe('isVariable', function () {
        it('should return true for a valid Variable instance', function () {
            expect(Variable.isVariable(new Variable({ type: 'string', key: 'foo', value: 'bar' }))).to.be.true;
        });

        it('should return false for a raw variable object', function () {
            expect(Variable.isVariable({ type: 'string', key: 'foo', value: 'bar' })).to.be.false;
        });

        it('should return false when called without arguments', function () {
            expect(Variable.isVariable()).to.be.false;
        });
    });
});
<|endoftext|>