<commit_before>var assert = require('assert');
var parse = require('../lib').parse;
var Parser = require('../lib').Parser;
var TYPE = require('../lib').Tokenizer.TYPE;
var toPlainObject = require('../lib').toPlainObject;
var walk = require('../lib').walk;
var translate = require('../lib').translate;
var forEachParseTest = require('./fixture/parse').forEachTest;
var stringify = require('./helpers/stringify');
var merge = require('./helpers').merge;

function repeat(str, count) {
    return new Array(count + 1).join(str);
}

function createParseErrorTest(name, test, options) {
    it(name + ' ' + JSON.stringify(test.source), function() {
        var error;

        assert.throws(function() {
            parse(test.source, options);
        }, function(e) {
            error = e;
            if (e.parseError) {
                return true;
            }
        }, 'Should be a CSS parse error');

        assert.equal(error.message, test.error);
        assert.deepEqual(error.parseError, test.position);
    });
}

describe('parse', function() {
    describe('basic', function() {
        forEachParseTest(function createParseTest(name, test) {
            it(name, function() {
                var ast = parse(test.source, test.options);

                // AST should be equal
                assert.equal(stringify(ast), stringify(test.ast));

                // translated AST should be equal to original source
                assert.equal(translate(ast), 'translate' in test ? test.translate : test.source);
            });
        });
    });

    describe('context', function() {
        it('wrong context', function() {
            assert.throws(function() {
                parse('a{}', { context: 'unknown' });
            }, 'Unknown context `unknown`');
        });
    });

    describe('errors', function() {
        forEachParseTest(function(name, test) {
            createParseErrorTest(name, test, merge(test.options, {
                positions: false
            }));
            createParseErrorTest(name + ' (with positions)', test, merge(test.options, {
                positions: true
            }));
        }, true);

        it('formattedMessage', function() {
            try {
                parse('/**/\n.\nfoo');
            } catch (e) {
                assert.equal(e.formattedMessage,
                    'Parse error: Identifier is expected\n' +
                    '    1 |/**/\n' +
                    '    2 |.\n' +
                    '--------^\n' +
                    '    3 |foo'
                );
                assert.equal(e.sourceFragment(),
                    '    2 |.\n' +
                    '--------^\n'
                );
                assert.equal(e.sourceFragment(3),
                    '    1 |/**/\n' +
                    '    2 |.\n' +
                    '--------^\n' +
                    '    3 |foo'
                );
            }
        });

        it('formattedMessage for source with long lines', function() {
            try {
                parse(
                    '/*' + repeat('1234567890', 20) + '*/\n' +
                    repeat(' ', 117) + '.\n' +
                    'foo\n' +
                    repeat(' ', 120) + 'bar'
                );
            } catch (e) {
                assert.equal(e.formattedMessage,
                    'Parse error: Identifier is expected\n' +
                    '    1 |…12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678…\n' +
                    '    2 |…                                                       .\n' +
                    '----------------------------------------------------------------^\n' +
                    '    3 |\n' +
                    '    4 |…                                                          bar'
                );
            }
        });
    });

    describe('positions', function() {
        it('should start with line 1 column 1 by default', function() {
            var ast = parse('.foo.bar {\n  property: value 123 123.4 .123 123px 99% #fff url( a ) / var( --a ), "test" \'test\';\n}', {
                positions: true
            });
            var positions = [];

            walk(ast, function(node) {
                if (node.loc) {
                    positions.push([
                        node.loc.start.offset,
                        node.loc.start.line,
                        node.loc.start.column,
                        node.type
                    ]);
                }
            });

            assert.deepEqual(positions, [
                [0, 1, 1, 'StyleSheet'],
                [0, 1, 1, 'Rule'],
                [0, 1, 1, 'SelectorList'],
                [0, 1, 1, 'Selector'],
                [0, 1, 1, 'ClassSelector'],
                [4, 1, 5, 'ClassSelector'],
                [9, 1, 10, 'Block'],
                [13, 2, 3, 'Declaration'],
                [22, 2, 12, 'Value'],
                [23, 2, 13, 'Identifier'],
                [29, 2, 19, 'Number'],
                [33, 2, 23, 'Number'],
                [39, 2, 29, 'Number'],
                [44, 2, 34, 'Dimension'],
                [50, 2, 40, 'Percentage'],
                [54, 2, 44, 'HexColor'],
                [59, 2, 49, 'Url'],
                [64, 2, 54, 'Raw'],
                [68, 2, 58, 'Operator'],
                [70, 2, 60, 'Function'],
                [75, 2, 65, 'Identifier'],
                [80, 2, 70, 'Operator'],
                [82, 2, 72, 'String'],
                [89, 2, 79, 'String']
            ]);
        });

        it('should start with specified offset, line and column', function() {
            var ast = parse('.foo.bar {\n  property: value 123 123.4 .123 123px 99% #fff url( a ) / var( --a ), "test" \'test\';\n}', {
                positions: true,
                offset: 100,
                line: 3,
                column: 5
            });
            var positions = [];

            walk(ast, function(node) {
                if (node.loc) {
                    positions.push([
                        node.loc.start.offset,
                        node.loc.start.line,
                        node.loc.start.column,
                        node.type
                    ]);
                }
            });

            assert.deepEqual(positions, [
                [100, 3, 5, 'StyleSheet'],
                [100, 3, 5, 'Rule'],
                [100, 3, 5, 'SelectorList'],
                [100, 3, 5, 'Selector'],
                [100, 3, 5, 'ClassSelector'],
                [104, 3, 9, 'ClassSelector'],
                [109, 3, 14, 'Block'],
                [113, 4, 3, 'Declaration'],
                [122, 4, 12, 'Value'],
                [123, 4, 13, 'Identifier'],
                [129, 4, 19, 'Number'],
                [133, 4, 23, 'Number'],
                [139, 4, 29, 'Number'],
                [144, 4, 34, 'Dimension'],
                [150, 4, 40, 'Percentage'],
                [154, 4, 44, 'HexColor'],
                [159, 4, 49, 'Url'],
                [164, 4, 54, 'Raw'],
                [168, 4, 58, 'Operator'],
                [170, 4, 60, 'Function'],
                [175, 4, 65, 'Identifier'],
                [180, 4, 70, 'Operator'],
                [182, 4, 72, 'String'],
                [189, 4, 79, 'String']
            ]);
        });
    });

    describe.skip('extension', function() {
        describe('value', function() {
            // var extended = new Parser();
            // var defaultGetNode = extended.scopeValue.getNode;
            // extended.scopeValue = merge(extended.scopeValue, {
            //     getNode: function(context) {
            //         if (this.scanner.tokenType === TYPE.DollarSign) {
            //             var start = this.scanner.tokenStart;
            //             this.scanner.next();

            //             return {
            //                 type: 'Variable',
            //                 loc: this.getLocation(start, this.scanner.tokenEnd),
            //                 name: this.scanner.consume(TYPE.Identifier)
            //             };
            //         }

            //         return defaultGetNode.call(this, context);
            //     }
            // });

            it('should fail by default', function() {
                assert.throws(function() {
                    parse('$a', {
                        context: 'value'
                    });
                }, /Unexpected input/);
            });

            it('should parse when extended', function() {
                var ast = extended.parse('$a', {
                    context: 'value'
                });

                assert.deepEqual(toPlainObject(ast), {
                    type: 'Value',
                    loc: null,
                    children: [
                        {
                            type: 'Variable',
                            loc: null,
                            name: 'a'
                        }
                    ]
                });
            });

            it('should fail on unknown', function() {
                assert.throws(function() {
                    extended.parse('@a', {
                        context: 'value'
                    });
                }, /Unexpected input/);
            });
        });

        describe('selector', function() {
            // var extended = new Parser();
            // var defaultGetNode = extended.scopeSelector.getNode;
            // extended.scopeSelector = {
            //     getNode: function(context) {
            //         if (this.scanner.tokenType === TYPE.Ampersand) {
            //             var start = this.scanner.tokenStart;
            //             this.scanner.next();

            //             return {
            //                 type: 'Nested',
            //                 loc: this.getLocation(start, this.scanner.tokenEnd)
            //             };
            //         }

            //         return defaultGetNode.call(this, context);
            //     }
            // };

            it('should fail by default', function() {
                assert.throws(function() {
                    parse('a &', {
                        context: 'selector'
                    });
                }, /Unexpected input/);
            });

            it('should parse when extended', function() {
                var ast = extended.parse('a &', {
                    context: 'selector'
                });

                assert.deepEqual(toPlainObject(ast), {
                    type: 'Selector',
                    loc: null,
                    children: [
                        {
                            type: 'TypeSelector',
                            loc: null,
                            name: 'a'
                        },
                        {
                            type: 'WhiteSpace',
                            loc: null,
                            value: ' '
                        },
                        {
                            type: 'Nested',
                            loc: null
                        }
                    ]
                });
            });

            it('should fail on unknown', function() {
                assert.throws(function() {
                    extended.parse('@a', {
                        context: 'selector'
                    });
                }, /Selector is expected/);
            });
        });
    });
});
<commit_msg>fix broken CI<commit_after>var assert = require('assert');
var parse = require('../lib').parse;
// var Parser = require('../lib').Parser;
// var TYPE = require('../lib').Tokenizer.TYPE;
// var toPlainObject = require('../lib').toPlainObject;
var walk = require('../lib').walk;
var translate = require('../lib').translate;
var forEachParseTest = require('./fixture/parse').forEachTest;
var stringify = require('./helpers/stringify');
var merge = require('./helpers').merge;

function repeat(str, count) {
    return new Array(count + 1).join(str);
}

function createParseErrorTest(name, test, options) {
    it(name + ' ' + JSON.stringify(test.source), function() {
        var error;

        assert.throws(function() {
            parse(test.source, options);
        }, function(e) {
            error = e;
            if (e.parseError) {
                return true;
            }
        }, 'Should be a CSS parse error');

        assert.equal(error.message, test.error);
        assert.deepEqual(error.parseError, test.position);
    });
}

describe('parse', function() {
    describe('basic', function() {
        forEachParseTest(function createParseTest(name, test) {
            it(name, function() {
                var ast = parse(test.source, test.options);

                // AST should be equal
                assert.equal(stringify(ast), stringify(test.ast));

                // translated AST should be equal to original source
                assert.equal(translate(ast), 'translate' in test ? test.translate : test.source);
            });
        });
    });

    describe('context', function() {
        it('wrong context', function() {
            assert.throws(function() {
                parse('a{}', { context: 'unknown' });
            }, 'Unknown context `unknown`');
        });
    });

    describe('errors', function() {
        forEachParseTest(function(name, test) {
            createParseErrorTest(name, test, merge(test.options, {
                positions: false
            }));
            createParseErrorTest(name + ' (with positions)', test, merge(test.options, {
                positions: true
            }));
        }, true);

        it('formattedMessage', function() {
            try {
                parse('/**/\n.\nfoo');
            } catch (e) {
                assert.equal(e.formattedMessage,
                    'Parse error: Identifier is expected\n' +
                    '    1 |/**/\n' +
                    '    2 |.\n' +
                    '--------^\n' +
                    '    3 |foo'
                );
                assert.equal(e.sourceFragment(),
                    '    2 |.\n' +
                    '--------^\n'
                );
                assert.equal(e.sourceFragment(3),
                    '    1 |/**/\n' +
                    '    2 |.\n' +
                    '--------^\n' +
                    '    3 |foo'
                );
            }
        });

        it('formattedMessage for source with long lines', function() {
            try {
                parse(
                    '/*' + repeat('1234567890', 20) + '*/\n' +
                    repeat(' ', 117) + '.\n' +
                    'foo\n' +
                    repeat(' ', 120) + 'bar'
                );
            } catch (e) {
                assert.equal(e.formattedMessage,
                    'Parse error: Identifier is expected\n' +
                    '    1 |…12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678…\n' +
                    '    2 |…                                                       .\n' +
                    '----------------------------------------------------------------^\n' +
                    '    3 |\n' +
                    '    4 |…                                                          bar'
                );
            }
        });
    });

    describe('positions', function() {
        it('should start with line 1 column 1 by default', function() {
            var ast = parse('.foo.bar {\n  property: value 123 123.4 .123 123px 99% #fff url( a ) / var( --a ), "test" \'test\';\n}', {
                positions: true
            });
            var positions = [];

            walk(ast, function(node) {
                if (node.loc) {
                    positions.push([
                        node.loc.start.offset,
                        node.loc.start.line,
                        node.loc.start.column,
                        node.type
                    ]);
                }
            });

            assert.deepEqual(positions, [
                [0, 1, 1, 'StyleSheet'],
                [0, 1, 1, 'Rule'],
                [0, 1, 1, 'SelectorList'],
                [0, 1, 1, 'Selector'],
                [0, 1, 1, 'ClassSelector'],
                [4, 1, 5, 'ClassSelector'],
                [9, 1, 10, 'Block'],
                [13, 2, 3, 'Declaration'],
                [22, 2, 12, 'Value'],
                [23, 2, 13, 'Identifier'],
                [29, 2, 19, 'Number'],
                [33, 2, 23, 'Number'],
                [39, 2, 29, 'Number'],
                [44, 2, 34, 'Dimension'],
                [50, 2, 40, 'Percentage'],
                [54, 2, 44, 'HexColor'],
                [59, 2, 49, 'Url'],
                [64, 2, 54, 'Raw'],
                [68, 2, 58, 'Operator'],
                [70, 2, 60, 'Function'],
                [75, 2, 65, 'Identifier'],
                [80, 2, 70, 'Operator'],
                [82, 2, 72, 'String'],
                [89, 2, 79, 'String']
            ]);
        });

        it('should start with specified offset, line and column', function() {
            var ast = parse('.foo.bar {\n  property: value 123 123.4 .123 123px 99% #fff url( a ) / var( --a ), "test" \'test\';\n}', {
                positions: true,
                offset: 100,
                line: 3,
                column: 5
            });
            var positions = [];

            walk(ast, function(node) {
                if (node.loc) {
                    positions.push([
                        node.loc.start.offset,
                        node.loc.start.line,
                        node.loc.start.column,
                        node.type
                    ]);
                }
            });

            assert.deepEqual(positions, [
                [100, 3, 5, 'StyleSheet'],
                [100, 3, 5, 'Rule'],
                [100, 3, 5, 'SelectorList'],
                [100, 3, 5, 'Selector'],
                [100, 3, 5, 'ClassSelector'],
                [104, 3, 9, 'ClassSelector'],
                [109, 3, 14, 'Block'],
                [113, 4, 3, 'Declaration'],
                [122, 4, 12, 'Value'],
                [123, 4, 13, 'Identifier'],
                [129, 4, 19, 'Number'],
                [133, 4, 23, 'Number'],
                [139, 4, 29, 'Number'],
                [144, 4, 34, 'Dimension'],
                [150, 4, 40, 'Percentage'],
                [154, 4, 44, 'HexColor'],
                [159, 4, 49, 'Url'],
                [164, 4, 54, 'Raw'],
                [168, 4, 58, 'Operator'],
                [170, 4, 60, 'Function'],
                [175, 4, 65, 'Identifier'],
                [180, 4, 70, 'Operator'],
                [182, 4, 72, 'String'],
                [189, 4, 79, 'String']
            ]);
        });
    });

    describe.skip('extension', function() {
        describe('value', function() {
            // var extended = new Parser();
            // var defaultGetNode = extended.scopeValue.getNode;
            // extended.scopeValue = merge(extended.scopeValue, {
            //     getNode: function(context) {
            //         if (this.scanner.tokenType === TYPE.DollarSign) {
            //             var start = this.scanner.tokenStart;
            //             this.scanner.next();

            //             return {
            //                 type: 'Variable',
            //                 loc: this.getLocation(start, this.scanner.tokenEnd),
            //                 name: this.scanner.consume(TYPE.Identifier)
            //             };
            //         }

            //         return defaultGetNode.call(this, context);
            //     }
            // });

            it('should fail by default', function() {
                assert.throws(function() {
                    parse('$a', {
                        context: 'value'
                    });
                }, /Unexpected input/);
            });

            // it('should parse when extended', function() {
            //     var ast = extended.parse('$a', {
            //         context: 'value'
            //     });

            //     assert.deepEqual(toPlainObject(ast), {
            //         type: 'Value',
            //         loc: null,
            //         children: [
            //             {
            //                 type: 'Variable',
            //                 loc: null,
            //                 name: 'a'
            //             }
            //         ]
            //     });
            // });

            // it('should fail on unknown', function() {
            //     assert.throws(function() {
            //         extended.parse('@a', {
            //             context: 'value'
            //         });
            //     }, /Unexpected input/);
            // });
        });

        describe('selector', function() {
            // var extended = new Parser();
            // var defaultGetNode = extended.scopeSelector.getNode;
            // extended.scopeSelector = {
            //     getNode: function(context) {
            //         if (this.scanner.tokenType === TYPE.Ampersand) {
            //             var start = this.scanner.tokenStart;
            //             this.scanner.next();

            //             return {
            //                 type: 'Nested',
            //                 loc: this.getLocation(start, this.scanner.tokenEnd)
            //             };
            //         }

            //         return defaultGetNode.call(this, context);
            //     }
            // };

            it('should fail by default', function() {
                assert.throws(function() {
                    parse('a &', {
                        context: 'selector'
                    });
                }, /Unexpected input/);
            });

            // it('should parse when extended', function() {
            //     var ast = extended.parse('a &', {
            //         context: 'selector'
            //     });

            //     assert.deepEqual(toPlainObject(ast), {
            //         type: 'Selector',
            //         loc: null,
            //         children: [
            //             {
            //                 type: 'TypeSelector',
            //                 loc: null,
            //                 name: 'a'
            //             },
            //             {
            //                 type: 'WhiteSpace',
            //                 loc: null,
            //                 value: ' '
            //             },
            //             {
            //                 type: 'Nested',
            //                 loc: null
            //             }
            //         ]
            //     });
            // });

            // it('should fail on unknown', function() {
            //     assert.throws(function() {
            //         extended.parse('@a', {
            //             context: 'selector'
            //         });
            //     }, /Selector is expected/);
            // });
        });
    });
});
<|endoftext|>


<commit_before>var request = require('supertest')
  , assert = require('assert')

var server = require('../server')
  , models = require('../app/models')

describe('Post API', function() {
  it('GET /v1/posts/this-post-does-not-exist should return 404', function(done) {
    request(server)
      .get('/v1/posts/this-post-does-not-exist')
      .expect(404, done)
  })

  it('GET /v1/posts/:postId should return json post', function(done) {
    var newPost = new models.Post({
      body: 'postBody',
      userId: 'userId'
    })
    newPost.save(function(post) {
      request(server)
        .get('/v1/posts/' + post.id)
        .expect('Content-Type', /json/)
        .expect(200)
        .end(function(err, res) {
          assert.equal(err, null)

          var jsonPost = res.body
          assert.equal(post.id, jsonPost.id)
          assert.equal(post.body, jsonPost.body)
          assert.equal(post.createdAt, jsonPost.createdAt)
          // Read defect in post.save() function
          // assert.equal(post.updatedAt, jsonPost.updatedAt)
          assert(Array.isArray(jsonPost.comments))
          assert(post.comments.length == jsonPost.comments.length)
          assert(Array.isArray(jsonPost.attachments))
          assert(post.attachments.length == jsonPost.attachments.length)

          done()
        })
    })
  })

  it('POST /v1/posts should create post and return json object', function(done) {
    var params = {
      body: 'postBody',
      userId: 'userId'
    }
    request(server)
      .post('/v1/posts')
      .send(params)
      .expect(200)
      .end(function(err, res) {
        assert.equal(err, null)

        var jsonPost = res.body
        assert(!!jsonPost.id)
        assert(!!jsonPost.createdAt)
        assert(!!jsonPost.updatedAt)
        assert.equal(params.body, jsonPost.body)
        // XXX: test userId
        assert(Array.isArray(jsonPost.comments))
        assert(jsonPost.comments.length == 0)
        assert(Array.isArray(jsonPost.attachments))
        assert(jsonPost.attachments.length == 0)

        done()
      })
  })

  it('POST /v1/posts with gif attachment should create post and return json object', function(done) {
    var params = {
      body: 'postBody',
      userId: 'userId'
    }
    request(server)
      .post('/v1/posts')
      .attach('file-0', 'test/fixtures/animated.gif')
      .field('body', params.body)
      .field('userId', params.userId)
      .expect(200)
      .end(function(err, res) {
        assert.equal(err, null)

        var jsonPost = res.body
        assert(!!jsonPost.id)
        assert(!!jsonPost.createdAt)
        assert(!!jsonPost.updatedAt)
        assert.equal(params.body, jsonPost.body)
        // XXX: test userId
        assert(Array.isArray(jsonPost.comments))
        assert(jsonPost.comments.length == 0)

        assert(Array.isArray(jsonPost.attachments))
        assert(jsonPost.attachments.length == 1)
        var jsonAttachment = jsonPost.attachments[0]
        assert(!!jsonAttachment.id)
        assert.equal(jsonAttachment.ext, 'gif')
        assert.equal(jsonAttachment.filename, 'animated.gif')
        // XXX
        // assert(!!jsonAttachment.fsPath)
        assert(!!jsonAttachment.path)

        assert(!!jsonAttachment.thumbnail)
        var jsonThumbnail = jsonAttachment.thumbnail
        assert(!!jsonThumbnail.id)
        assert.equal(jsonThumbnail.ext, 'gif')
        assert.equal(jsonThumbnail.filename, 'animated.gif')
        assert(!!jsonThumbnail.path)
        // XXX
        // assert(!!jsonThumbnail.fsPath)

        done()
      })
  })

  it('POST /v1/posts with zip attachment should create post and return json object without attachment', function(done) {
    var params = {
      body: 'postBody',
      userId: 'userId'
    }
    request(server)
      .post('/v1/posts')
      .attach('file-0', 'test/fixtures/blank.zip')
      .field('body', params.body)
      .field('userId', params.userId)
      .expect(200)
      .end(function(err, res) {
        assert.equal(err, null)

        var jsonPost = res.body
        assert(!!jsonPost.id)
        assert(!!jsonPost.createdAt)
        assert(!!jsonPost.updatedAt)
        assert.equal(params.body, jsonPost.body)
        // XXX: test userId
        assert(Array.isArray(jsonPost.comments))
        assert(jsonPost.comments.length == 0)
        assert(Array.isArray(jsonPost.attachments))
        assert(jsonPost.attachments.length == 0)

        done()
      })
  })

  it('POST /v1/posts with missing body should return 422'
     // , function(done) {
     //   var params = {
     //     userId: 'userId'
     //   }
     //   request(server)
     //     .post('/v1/posts')
     //     .send(params)
     //     .expect(422, done)
     // }
    )

  it('POST /v1/posts with missing userId should return 422'
     // , function(done) {
     //   var params = {
     //     body: 'postBody'
     //   }
     //   request(server)
     //     .post('/v1/posts')
     //     .send(params)
     //     .expect(422, done)
     // }
    )
})
<commit_msg>Changes assert(test == value) to assert.equal(test, value)<commit_after>var request = require('supertest')
  , assert = require('assert')

var server = require('../server')
  , models = require('../app/models')

describe('Post API', function() {
  it('GET /v1/posts/this-post-does-not-exist should return 404', function(done) {
    request(server)
      .get('/v1/posts/this-post-does-not-exist')
      .expect(404, done)
  })

  it('GET /v1/posts/:postId should return json post', function(done) {
    var newPost = new models.Post({
      body: 'postBody',
      userId: 'userId'
    })
    newPost.save(function(post) {
      request(server)
        .get('/v1/posts/' + post.id)
        .expect('Content-Type', /json/)
        .expect(200)
        .end(function(err, res) {
          assert.equal(err, null)

          var jsonPost = res.body
          assert.equal(post.id, jsonPost.id)
          assert.equal(post.body, jsonPost.body)
          assert.equal(post.createdAt, jsonPost.createdAt)
          // Read defect in post.save() function
          // assert.equal(post.updatedAt, jsonPost.updatedAt)
          assert(Array.isArray(jsonPost.comments))
          assert(post.comments.length == jsonPost.comments.length)
          assert(Array.isArray(jsonPost.attachments))
          assert(post.attachments.length == jsonPost.attachments.length)

          done()
        })
    })
  })

  it('POST /v1/posts should create post and return json object', function(done) {
    var params = {
      body: 'postBody',
      userId: 'userId'
    }
    request(server)
      .post('/v1/posts')
      .send(params)
      .expect(200)
      .end(function(err, res) {
        assert.equal(err, null)

        var jsonPost = res.body
        assert(!!jsonPost.id)
        assert(!!jsonPost.createdAt)
        assert(!!jsonPost.updatedAt)
        assert.equal(params.body, jsonPost.body)
        // XXX: test userId
        assert(Array.isArray(jsonPost.comments))
        assert.equal(jsonPost.comments.length, 0)
        assert(Array.isArray(jsonPost.attachments))
        assert.equal(jsonPost.attachments.length, 0)

        done()
      })
  })

  it('POST /v1/posts with gif attachment should create post and return json object', function(done) {
    var params = {
      body: 'postBody',
      userId: 'userId'
    }
    request(server)
      .post('/v1/posts')
      .attach('file-0', 'test/fixtures/animated.gif')
      .field('body', params.body)
      .field('userId', params.userId)
      .expect(200)
      .end(function(err, res) {
        assert.equal(err, null)

        var jsonPost = res.body
        assert(!!jsonPost.id)
        assert(!!jsonPost.createdAt)
        assert(!!jsonPost.updatedAt)
        assert.equal(params.body, jsonPost.body)
        // XXX: test userId
        assert(Array.isArray(jsonPost.comments))
        assert.equal(jsonPost.comments.length, 0)

        assert(Array.isArray(jsonPost.attachments))
        assert.equal(jsonPost.attachments.length, 1)
        var jsonAttachment = jsonPost.attachments[0]
        assert(!!jsonAttachment.id)
        assert.equal(jsonAttachment.ext, 'gif')
        assert.equal(jsonAttachment.filename, 'animated.gif')
        // XXX
        // assert(!!jsonAttachment.fsPath)
        assert(!!jsonAttachment.path)

        assert(!!jsonAttachment.thumbnail)
        var jsonThumbnail = jsonAttachment.thumbnail
        assert(!!jsonThumbnail.id)
        assert.equal(jsonThumbnail.ext, 'gif')
        assert.equal(jsonThumbnail.filename, 'animated.gif')
        assert(!!jsonThumbnail.path)
        // XXX
        // assert(!!jsonThumbnail.fsPath)

        done()
      })
  })

  it('POST /v1/posts with zip attachment should create post and return json object without attachment', function(done) {
    var params = {
      body: 'postBody',
      userId: 'userId'
    }
    request(server)
      .post('/v1/posts')
      .attach('file-0', 'test/fixtures/blank.zip')
      .field('body', params.body)
      .field('userId', params.userId)
      .expect(200)
      .end(function(err, res) {
        assert.equal(err, null)

        var jsonPost = res.body
        assert(!!jsonPost.id)
        assert(!!jsonPost.createdAt)
        assert(!!jsonPost.updatedAt)
        assert.equal(params.body, jsonPost.body)
        // XXX: test userId
        assert(Array.isArray(jsonPost.comments))
        assert.equal(jsonPost.comments.length, 0)
        assert(Array.isArray(jsonPost.attachments))
        assert.equal(jsonPost.attachments.length, 0)

        done()
      })
  })

  it('POST /v1/posts with missing body should return 422'
     // , function(done) {
     //   var params = {
     //     userId: 'userId'
     //   }
     //   request(server)
     //     .post('/v1/posts')
     //     .send(params)
     //     .expect(422, done)
     // }
    )

  it('POST /v1/posts with missing userId should return 422'
     // , function(done) {
     //   var params = {
     //     body: 'postBody'
     //   }
     //   request(server)
     //     .post('/v1/posts')
     //     .send(params)
     //     .expect(422, done)
     // }
    )
})
<|endoftext|>


<commit_before>'use strict';

const assert = require('assert');
const Query = require('../lib/query');

describe('Query', function() {
  describe('#compile()', function() {
    it('should return a query function', function() {
      const q = new Query([]).compile();
      assert.equal(typeof q, 'function');
    });
  });

  describe('#execute()', function() {
    it('should return query object', function() {
      const q = new Query([]).compile();
      assert(Object.keys(q()).indexOf('text') > -1);
      assert(Object.keys(q()).indexOf('values') > -1);
    });

    describe('Constants', function() {
      it('should compile strings', function() {
        const query = new Query(['SELECT', 'TRUE']);
        const q = query.compile();
        assert.deepEqual(query._compiledQuery, [
          { text: 'SELECT', values: [] },
          { text: 'TRUE', values: [] }
        ]);

        const qs = q();
        assert.equal(qs.text, 'SELECT TRUE');
        assert.deepEqual(qs.values, []);
      });

      it('should compile numbers into strings', function() {
        const query = new Query(['SELECT', 3]);
        const q = query.compile();
        assert.deepEqual(query._compiledQuery, [
          { text: 'SELECT', values: [] },
          { text: '3', values: [] }
        ]);

        const qs = q();
        assert.equal(qs.text, 'SELECT 3');
        assert.deepEqual(qs.values, []);
      });

      it('should compile arrays into comma seperated lists wrapped in quotes', function() {
        const query = new Query(['SELECT', ['id', 'email', 'password']]);
        const q = query.compile();
        assert.deepEqual(query._compiledQuery, [
          { text: 'SELECT', values: [] },
          { text: '"id", "email", "password"', values: [] }
        ]);

        const qs = q();
        assert.equal(qs.text, 'SELECT "id", "email", "password"');
        assert.deepEqual(qs.values, []);
      });
    });

    describe('Fragment: equals', function() {
      it('should compile equals fragment into a paramaterized query', function() {
        const query = new Query([
          'SELECT * FROM foobar WHERE',
          { equals: { join: 'AND' } }
        ]);
        const q = query.compile();
        const qs = q({ email: 'foo@bar.com', active: true });
        assert.equal(qs.text, 'SELECT * FROM foobar WHERE "email" = $1 AND "active" = $2');
        assert.deepEqual(qs.values, ['foo@bar.com', true]);
      });

      it('should compile equals fragment with skip into a paramaterized query', function() {
        const query = new Query([
          'UPDATE foobar', 'SET',
          { equals: { join: ',', skip: ['id'] } },
          'WHERE id = 4'
        ]);
        const q = query.compile();
        const qs = q({ id: 4, email: 'foo@bar.com', active: true });
        assert.equal(qs.text, 'UPDATE foobar SET "email" = $1 , "active" = $2 WHERE id = 4');
        assert.deepEqual(qs.values, ['foo@bar.com', true]);
      });

      it('should compile multiple equals fragment into a paramaterized query', function() {
        const query = new Query([
          'SELECT * FROM foobar WHERE',
          { equals: { join: 'AND' } },
          { equals: { join: 'AND' } }
        ]);
        const q = query.compile();
        const qs = q({ email: 'foo@bar.com', active: true });
        assert.equal(qs.text, 'SELECT * FROM foobar WHERE "email" = $1 AND "active" = $2 "email" = $3 AND "active" = $4');
        assert.deepEqual(qs.values, ['foo@bar.com', true, 'foo@bar.com', true]);
      });

      it('should compile equals fragment into empty string if no args', function() {
        const query = new Query([
          'SELECT * FROM foobar WHERE',
          { equals: { join: 'AND', whenEmpty: 'TRUE' } }
        ]);
        const q = query.compile();
        const qs = q();
        assert.equal(qs.text, 'SELECT * FROM foobar WHERE TRUE');
        assert.deepEqual(qs.values, []);
      });

      it('should throw an Error if empty args and no empty state given', function() {
        const query = new Query([
          'SELECT * FROM foobar WHERE',
          { equals: { join: 'AND' } }
        ]);
        const q = query.compile();
        assert.throws(function() {
          const qs = q();
        }, /Empty/);
      });
    });

    describe('Fragment: value', function() {
      it('should compile value fragment into a parameterized string', function() {
        const query = new Query([
          'SELECT * FROM foobar WHERE "id" =',
          { value: 'id' },
          'AND "active" =',
          { value: 'active' }
        ]);
        const q = query.compile();
        const qs = q({ id: 4, active: true });
        assert.equal(qs.text, 'SELECT * FROM foobar WHERE "id" = $1 AND "active" = $2');
        assert.deepEqual(qs.values, [4, true]);
      });

      it('should throw an Error if value arg is missing', function() {
        const query = new Query([
          'SELECT * FROM foobar WHERE "id" =',
          { value: 'id' }
        ]);
        const q = query.compile();
        assert.throws(function() {
          const qs = q();
        }, /Missing/);
      });
    });

    describe('Complex queries', function() {
      it('query with value and equals fragments', function() {
        const query = new Query([
          'UPDATE', 'users',
          'SET', { equals: { join: ',', skip: ['id'] } },
          'WHERE "id" =', { value: 'id' }
        ]);
        const q = query.compile();
        const qs = q({ id: 4, email: 'foo@bar.com', active: true });
        assert.equal(qs.text, 'UPDATE users SET "email" = $1 , "active" = $2 WHERE "id" = $3');
        assert.deepEqual(qs.values, ['foo@bar.com', true, 4]);
      });
    });
  });
});
<commit_msg>Added test for custom fragments<commit_after>'use strict';

const assert = require('assert');
const Query = require('../lib/query');

describe('Query', function() {
  describe('#compile()', function() {
    it('should return a query function', function() {
      const q = new Query([]).compile();
      assert.equal(typeof q, 'function');
    });
  });

  describe('#execute()', function() {
    it('should return query object', function() {
      const q = new Query([]).compile();
      assert(Object.keys(q()).indexOf('text') > -1);
      assert(Object.keys(q()).indexOf('values') > -1);
    });

    describe('Constants', function() {
      it('should compile strings', function() {
        const query = new Query(['SELECT', 'TRUE']);
        const q = query.compile();
        assert.deepEqual(query._compiledQuery, [
          { text: 'SELECT', values: [] },
          { text: 'TRUE', values: [] }
        ]);

        const qs = q();
        assert.equal(qs.text, 'SELECT TRUE');
        assert.deepEqual(qs.values, []);
      });

      it('should compile numbers into strings', function() {
        const query = new Query(['SELECT', 3]);
        const q = query.compile();
        assert.deepEqual(query._compiledQuery, [
          { text: 'SELECT', values: [] },
          { text: '3', values: [] }
        ]);

        const qs = q();
        assert.equal(qs.text, 'SELECT 3');
        assert.deepEqual(qs.values, []);
      });

      it('should compile arrays into comma seperated lists wrapped in quotes', function() {
        const query = new Query(['SELECT', ['id', 'email', 'password']]);
        const q = query.compile();
        assert.deepEqual(query._compiledQuery, [
          { text: 'SELECT', values: [] },
          { text: '"id", "email", "password"', values: [] }
        ]);

        const qs = q();
        assert.equal(qs.text, 'SELECT "id", "email", "password"');
        assert.deepEqual(qs.values, []);
      });
    });

    describe('Fragment: equals', function() {
      it('should compile equals fragment into a paramaterized query', function() {
        const query = new Query([
          'SELECT * FROM foobar WHERE',
          { equals: { join: 'AND' } }
        ]);
        const q = query.compile();
        const qs = q({ email: 'foo@bar.com', active: true });
        assert.equal(qs.text, 'SELECT * FROM foobar WHERE "email" = $1 AND "active" = $2');
        assert.deepEqual(qs.values, ['foo@bar.com', true]);
      });

      it('should compile equals fragment with skip into a paramaterized query', function() {
        const query = new Query([
          'UPDATE foobar', 'SET',
          { equals: { join: ',', skip: ['id'] } },
          'WHERE id = 4'
        ]);
        const q = query.compile();
        const qs = q({ id: 4, email: 'foo@bar.com', active: true });
        assert.equal(qs.text, 'UPDATE foobar SET "email" = $1 , "active" = $2 WHERE id = 4');
        assert.deepEqual(qs.values, ['foo@bar.com', true]);
      });

      it('should compile multiple equals fragment into a paramaterized query', function() {
        const query = new Query([
          'SELECT * FROM foobar WHERE',
          { equals: { join: 'AND' } },
          { equals: { join: 'AND' } }
        ]);
        const q = query.compile();
        const qs = q({ email: 'foo@bar.com', active: true });
        assert.equal(qs.text, 'SELECT * FROM foobar WHERE "email" = $1 AND "active" = $2 "email" = $3 AND "active" = $4');
        assert.deepEqual(qs.values, ['foo@bar.com', true, 'foo@bar.com', true]);
      });

      it('should compile equals fragment into empty string if no args', function() {
        const query = new Query([
          'SELECT * FROM foobar WHERE',
          { equals: { join: 'AND', whenEmpty: 'TRUE' } }
        ]);
        const q = query.compile();
        const qs = q();
        assert.equal(qs.text, 'SELECT * FROM foobar WHERE TRUE');
        assert.deepEqual(qs.values, []);
      });

      it('should throw an Error if empty args and no empty state given', function() {
        const query = new Query([
          'SELECT * FROM foobar WHERE',
          { equals: { join: 'AND' } }
        ]);
        const q = query.compile();
        assert.throws(function() {
          const qs = q();
        }, /Empty/);
      });
    });

    describe('Fragment: value', function() {
      it('should compile value fragment into a parameterized string', function() {
        const query = new Query([
          'SELECT * FROM foobar WHERE "id" =',
          { value: 'id' },
          'AND "active" =',
          { value: 'active' }
        ]);
        const q = query.compile();
        const qs = q({ id: 4, active: true });
        assert.equal(qs.text, 'SELECT * FROM foobar WHERE "id" = $1 AND "active" = $2');
        assert.deepEqual(qs.values, [4, true]);
      });

      it('should throw an Error if value arg is missing', function() {
        const query = new Query([
          'SELECT * FROM foobar WHERE "id" =',
          { value: 'id' }
        ]);
        const q = query.compile();
        assert.throws(function() {
          const qs = q();
        }, /Missing/);
      });
    });

    describe('Fragment: custom', function() {
      it('should compile custom fragmnt into a parameterized string', function() {
        const query = new Query([
          'SELECT * FROM users WHERE "id" =',
          function (args, paramIndexStart) {
            return {
              text: '$1',
              values: [10]
            };
          },
          'ORDER BY id'
        ]);
        const q = query.compile();
        const qs = q();
        assert.equal(qs.text, 'SELECT * FROM users WHERE "id" = $1 ORDER BY id');
        assert.deepEqual(qs.values, [10]);
      });
    });

    describe('Complex queries', function() {
      it('query with value and equals fragments', function() {
        const query = new Query([
          'UPDATE', 'users',
          'SET', { equals: { join: ',', skip: ['id'] } },
          'WHERE "id" =', { value: 'id' }
        ]);
        const q = query.compile();
        const qs = q({ id: 4, email: 'foo@bar.com', active: true });
        assert.equal(qs.text, 'UPDATE users SET "email" = $1 , "active" = $2 WHERE "id" = $3');
        assert.deepEqual(qs.values, ['foo@bar.com', true, 4]);
      });
    });
  });
});
<|endoftext|>


<commit_before>/**
 * Copyright 2014 GetHuman LLC
 * Author: Jeff Whelpley
 * Date: 10/21/14
 *
 *
 */
var newrelic = require('newrelic');
var _ = require('lodash');
var routeHandler;

/**
 * Add web routes
 * @param opts
 */
function addWebRoutes(opts) {
    var server = opts.server;
    var socialProviders = opts.config && opts.config.security && opts.config.security.social;
    var me = this;

    // first add social auth routes
    _.each(socialProviders, function (provider, providerName) {
        server.route({
            method:     ['GET', 'POST'],
            path:       '/auth/' + providerName,
            handler:    me.getWebRouteHandler(opts),
            config:     { auth: providerName }
        });
    });

    routeHandler = this.getWebRouteHandler(opts);

    // all other URLs checked against app config files for dymanic web routes
    server.route({
        method:     '*',
        path:       '/{p*}',
        handler:    routeHandler,
        config: {
            state: {
                parse:      true,
                failAction: 'log'
            }
        }
    });
}

/**
 * The idea here is to use the same request and reply
 * but a different route handler based on the given URL override
 */
function processRoute(opts) {
    if (routeHandler) {
        routeHandler(opts.request, opts.reply, opts.urlOverride);
    }
    else {
        opts.reply.continue();
    }
}

/**
 * Execute the server preprocessing method and return true if the preprocessor
 * handled the request
 *
 * @param request
 * @param reply
 * @returns {Function}
 */
function getServerPreProcessing(request, reply) {
    var me = this;

    return function (routeInfo, page, model) {
        if (!page.serverPreprocessing) { return false; }

        var deps = { request: request, reply: reply, model: model, routeInfo: routeInfo };
        return me.pancakes.cook(page.serverPreprocessing, { dependencies: deps });
    };
}

/**
 * Render the web route for one of the dynamic routes. These routes are based off of
 * the .app config files for each app.
 *
 * @param opts
 */
function getWebRouteHandler(opts) {
    var webRouteHandler = this.pancakes.webRouteHandler;
    var me = this;

    return function handleWebRoute(request, reply, urlOverride) {
        var appName = request.app.name;
        var url = urlOverride || request.url.pathname;
        var routeInfo = webRouteHandler.getRouteInfo(appName, url, request.query, request.app.lang);

        newrelic.setTransactionName(routeInfo.appName + '||' + routeInfo.lang + '||' + routeInfo.url);

        // if redirect URL exists, just redirect right away
        if (routeInfo.redirect) {

            // make sure tokens in the redirect URL are replaced
            var redirectUrl = routeInfo.redirect;
            _.each(routeInfo.tokens, function (value, key) {
                redirectUrl = redirectUrl.replace('{' + key + '}', value);
            });

            if (redirectUrl.indexOf('{') >= 0) {
                throw new Error('Redirect URL has token that was not replaced: ' + redirectUrl);
            }

            reply().redirect(redirectUrl).permanent(true);
            return;
        }

        // if the app is handling this request, don't do anything
        if (opts.preProcess && opts.preProcess(request, reply)) { return; }

        var callbacks = {
            serverPreprocessing:    me.getServerPreProcessing(request, reply),
            addToModel:             opts.addToModel,
            pageCacheService:       opts.pageCacheService
        };

        webRouteHandler.processWebRequest(routeInfo, callbacks)
            .then(function (renderedPage) {

                // if no rendered page, it means request was handled earlier somewhere (ex. serverPreprocess redirect)
                if (!renderedPage) {
                    return;
                }

                var response = reply(renderedPage);

                // add content type if in the route info
                if (routeInfo.contentType) {
                    response.header('Content-Type', routeInfo.contentType);
                }

                // as long as the nocache attribute is NOT set, add cache headers (sorry for double negative :-))
                if (!routeInfo.nocache) {
                    response.header('cache-control', 'public, max-age=60');
                }
            })
            .done();    // throw error up to app level error handler
    };
}

module.exports = {
    processRoute: processRoute,
    addWebRoutes: addWebRoutes,
    getWebRouteHandler: getWebRouteHandler,
    getServerPreProcessing: getServerPreProcessing
};<commit_msg>Moving newrelic down so wont break batch<commit_after>/**
 * Copyright 2014 GetHuman LLC
 * Author: Jeff Whelpley
 * Date: 10/21/14
 *
 *
 */
var _ = require('lodash');
var routeHandler;

/**
 * Add web routes
 * @param opts
 */
function addWebRoutes(opts) {
    var server = opts.server;
    var socialProviders = opts.config && opts.config.security && opts.config.security.social;
    var me = this;

    // first add social auth routes
    _.each(socialProviders, function (provider, providerName) {
        server.route({
            method:     ['GET', 'POST'],
            path:       '/auth/' + providerName,
            handler:    me.getWebRouteHandler(opts),
            config:     { auth: providerName }
        });
    });

    routeHandler = this.getWebRouteHandler(opts);

    // all other URLs checked against app config files for dymanic web routes
    server.route({
        method:     '*',
        path:       '/{p*}',
        handler:    routeHandler,
        config: {
            state: {
                parse:      true,
                failAction: 'log'
            }
        }
    });
}

/**
 * The idea here is to use the same request and reply
 * but a different route handler based on the given URL override
 */
function processRoute(opts) {
    if (routeHandler) {
        routeHandler(opts.request, opts.reply, opts.urlOverride);
    }
    else {
        opts.reply.continue();
    }
}

/**
 * Execute the server preprocessing method and return true if the preprocessor
 * handled the request
 *
 * @param request
 * @param reply
 * @returns {Function}
 */
function getServerPreProcessing(request, reply) {
    var me = this;

    return function (routeInfo, page, model) {
        if (!page.serverPreprocessing) { return false; }

        var deps = { request: request, reply: reply, model: model, routeInfo: routeInfo };
        return me.pancakes.cook(page.serverPreprocessing, { dependencies: deps });
    };
}

/**
 * Render the web route for one of the dynamic routes. These routes are based off of
 * the .app config files for each app.
 *
 * @param opts
 */
function getWebRouteHandler(opts) {
    var webRouteHandler = this.pancakes.webRouteHandler;
    var me = this;

    return function handleWebRoute(request, reply, urlOverride) {
        var appName = request.app.name;
        var url = urlOverride || request.url.pathname;
        var routeInfo = webRouteHandler.getRouteInfo(appName, url, request.query, request.app.lang);

        var newrelic = require('newrelic');
        newrelic.setTransactionName(routeInfo.appName + '||' + routeInfo.lang + '||' + routeInfo.url);

        // if redirect URL exists, just redirect right away
        if (routeInfo.redirect) {

            // make sure tokens in the redirect URL are replaced
            var redirectUrl = routeInfo.redirect;
            _.each(routeInfo.tokens, function (value, key) {
                redirectUrl = redirectUrl.replace('{' + key + '}', value);
            });

            if (redirectUrl.indexOf('{') >= 0) {
                throw new Error('Redirect URL has token that was not replaced: ' + redirectUrl);
            }

            reply().redirect(redirectUrl).permanent(true);
            return;
        }

        // if the app is handling this request, don't do anything
        if (opts.preProcess && opts.preProcess(request, reply)) { return; }

        var callbacks = {
            serverPreprocessing:    me.getServerPreProcessing(request, reply),
            addToModel:             opts.addToModel,
            pageCacheService:       opts.pageCacheService
        };

        webRouteHandler.processWebRequest(routeInfo, callbacks)
            .then(function (renderedPage) {

                // if no rendered page, it means request was handled earlier somewhere (ex. serverPreprocess redirect)
                if (!renderedPage) {
                    return;
                }

                var response = reply(renderedPage);

                // add content type if in the route info
                if (routeInfo.contentType) {
                    response.header('Content-Type', routeInfo.contentType);
                }

                // as long as the nocache attribute is NOT set, add cache headers (sorry for double negative :-))
                if (!routeInfo.nocache) {
                    response.header('cache-control', 'public, max-age=60');
                }
            })
            .done();    // throw error up to app level error handler
    };
}

module.exports = {
    processRoute: processRoute,
    addWebRoutes: addWebRoutes,
    getWebRouteHandler: getWebRouteHandler,
    getServerPreProcessing: getServerPreProcessing
};<|endoftext|>


<commit_before>// Copyright (c) Rotorz Limited. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root.

import path from "path";

import given from "mocha-testdata";
import should from "should";

import {Environment} from "webreed-core/lib/Environment";
import {ResourceType} from "webreed-core/lib/ResourceType";

import {BinaryMode} from "webreed-binary-mode/lib/BinaryMode";
import {JsonHandler} from "webreed-json-handler/lib/JsonHandler";
import {NunjucksTemplateEngine} from "webreed-nunjucks-template-engine/lib/NunjucksTemplateEngine";
import {StandardGenerator} from "webreed-standard-generator/lib/StandardGenerator";
import {TemplateTransformer} from "webreed-template-transformer/lib/TemplateTransformer";
import {TextMode} from "webreed-text-mode/lib/TextMode";
import {YamlHandler} from "webreed-yaml-handler/lib/YamlHandler";

import setup from "../lib/setup";

describe("#setup(projectRootPath, [options])", function () {

  beforeEach(function () {
    this.projectRootPath = path.join(__dirname, "fixtures/example-project");
    this.env = setup(this.projectRootPath);
  });

  it("is a function", function () {
    setup
      .should.be.a.Function();
  });

  it("is named 'setup'", function () {
    setup.name
      .should.be.eql("setup");
  });

  given( undefined, null, 42 ).
  it("throws error when argument 'projectRootPath' is not a string", function (projectRootPath) {
    (() => setup(projectRootPath))
      .should.throw("argument 'projectRootPath' must be a string");
  });

  given( "", "   ", "\t  " ).
  it("throws error when argument 'projectRootPath' is an empty string", function (projectRootPath) {
    (() => setup(projectRootPath))
      .should.throw("argument 'projectRootPath' must be a non-empty string");
  });

  given( 42, "" ).
  it("throws error when argument 'config' is not a string", function (config) {
    (() => setup(this.projectRootPath, config))
      .should.throw("argument 'config' must be `null` or an object");
  });

  it("returns a webreed environment", function () {
    this.env
      .should.be.instanceOf(Environment);
  });

  it("sets root path of project in environment", function () {
    this.env.projectRootPath
      .should.be.eql(this.projectRootPath);
  });

  it("includes the fallback resource type '*'", function () {
    this.env.resourceTypes.get("*")
      .should.be.instanceOf(ResourceType);
  });

  it("fallback resource type '*' assumes 'binary' mode", function () {
    this.env.resourceTypes.get("*").mode
      .should.be.eql("binary");
  });

  it("includes the 'binary' mode by default", function () {
    this.env.modes.get("binary")
      .should.be.instanceOf(BinaryMode);
  });

  it("includes the 'text' mode by default", function () {
    this.env.modes.get("text")
      .should.be.instanceOf(TextMode);
  });

  it("includes the 'standard' generator by default", function () {
    this.env.generators.get("standard")
      .should.be.instanceOf(StandardGenerator);
  });

  it("includes the 'template' transformer by default", function () {
    this.env.transformers.get("template")
      .should.be.instanceOf(TemplateTransformer);
  });

  it("includes the 'json' handler by default", function () {
    this.env.handlers.get("json")
      .should.be.instanceOf(JsonHandler);
  });

  it("includes the 'yaml' handler by default", function () {
    this.env.handlers.get("yaml")
      .should.be.instanceOf(YamlHandler);
  });

  it("includes the 'nunjucks' template engine by default", function () {
    this.env.templateEngines.get("nunjucks")
      .should.be.instanceOf(NunjucksTemplateEngine);
  });

});
<commit_msg>Correct unit test description.<commit_after>// Copyright (c) Rotorz Limited. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root.

import path from "path";

import given from "mocha-testdata";
import should from "should";

import {Environment} from "webreed-core/lib/Environment";
import {ResourceType} from "webreed-core/lib/ResourceType";

import {BinaryMode} from "webreed-binary-mode/lib/BinaryMode";
import {JsonHandler} from "webreed-json-handler/lib/JsonHandler";
import {NunjucksTemplateEngine} from "webreed-nunjucks-template-engine/lib/NunjucksTemplateEngine";
import {StandardGenerator} from "webreed-standard-generator/lib/StandardGenerator";
import {TemplateTransformer} from "webreed-template-transformer/lib/TemplateTransformer";
import {TextMode} from "webreed-text-mode/lib/TextMode";
import {YamlHandler} from "webreed-yaml-handler/lib/YamlHandler";

import setup from "../lib/setup";

describe("#setup(projectRootPath, [options])", function () {

  beforeEach(function () {
    this.projectRootPath = path.join(__dirname, "fixtures/example-project");
    this.env = setup(this.projectRootPath);
  });

  it("is a function", function () {
    setup
      .should.be.a.Function();
  });

  it("is named 'setup'", function () {
    setup.name
      .should.be.eql("setup");
  });

  given( undefined, null, 42 ).
  it("throws error when argument 'projectRootPath' is not a string", function (projectRootPath) {
    (() => setup(projectRootPath))
      .should.throw("argument 'projectRootPath' must be a string");
  });

  given( "", "   ", "\t  " ).
  it("throws error when argument 'projectRootPath' is an empty string", function (projectRootPath) {
    (() => setup(projectRootPath))
      .should.throw("argument 'projectRootPath' must be a non-empty string");
  });

  given( 42, "" ).
  it("throws error when argument 'config' is not an object", function (config) {
    (() => setup(this.projectRootPath, config))
      .should.throw("argument 'config' must be `null` or an object");
  });

  it("returns a webreed environment", function () {
    this.env
      .should.be.instanceOf(Environment);
  });

  it("sets root path of project in environment", function () {
    this.env.projectRootPath
      .should.be.eql(this.projectRootPath);
  });

  it("includes the fallback resource type '*'", function () {
    this.env.resourceTypes.get("*")
      .should.be.instanceOf(ResourceType);
  });

  it("fallback resource type '*' assumes 'binary' mode", function () {
    this.env.resourceTypes.get("*").mode
      .should.be.eql("binary");
  });

  it("includes the 'binary' mode by default", function () {
    this.env.modes.get("binary")
      .should.be.instanceOf(BinaryMode);
  });

  it("includes the 'text' mode by default", function () {
    this.env.modes.get("text")
      .should.be.instanceOf(TextMode);
  });

  it("includes the 'standard' generator by default", function () {
    this.env.generators.get("standard")
      .should.be.instanceOf(StandardGenerator);
  });

  it("includes the 'template' transformer by default", function () {
    this.env.transformers.get("template")
      .should.be.instanceOf(TemplateTransformer);
  });

  it("includes the 'json' handler by default", function () {
    this.env.handlers.get("json")
      .should.be.instanceOf(JsonHandler);
  });

  it("includes the 'yaml' handler by default", function () {
    this.env.handlers.get("yaml")
      .should.be.instanceOf(YamlHandler);
  });

  it("includes the 'nunjucks' template engine by default", function () {
    this.env.templateEngines.get("nunjucks")
      .should.be.instanceOf(NunjucksTemplateEngine);
  });

});
<|endoftext|>


<commit_before>const sinon = require('sinon');
const mocha = require('mocha');

beforeEach(function () {
  this.sandbox = sinon.sandbox.create();
});

afterEach(function () {
  this.sandbox.restore();
});
<commit_msg>test(sinon): sinon chai connector<commit_after>const sinon = require('sinon');
const mocha = require('mocha');
const chai = require('chai');
const sinonChai = require('sinon-chai');

chai.use(sinonChai);

beforeEach(function () {
  this.sandbox = sinon.sandbox.create();
});

afterEach(function () {
  this.sandbox.restore();
});
<|endoftext|>


<commit_before>'use strict';

const utils = require( '../../utils' );

const JWTConfiguration = require( './configuration' );

const validator = require( './validator' );

const Plugin = require( '../plugin' );

function configureFromEnvVars( instance ) {

    let options = {};

    options.algorithm = process.env.VANDIUM_JWT_ALGORITHM;
    options.secret = process.env.VANDIUM_JWT_SECRET;
    options.public_key = process.env.VANDIUM_JWT_PUBKEY;
    options.token_name = process.env.VANDIUM_JWT_TOKEN_NAME;

    if( process.env.VANDIUM_JWT_USE_XSRF ) {

        options.xsrf = utils.parseBoolean( process.env.VANDIUM_JWT_USE_XSRF );
    }

    options.xsrf_token_name = process.env.VANDIUM_JWT_XSRF_TOKEN_NAME;
    options.xsrf_claim_name = process.env.VANDIUM_JWT_XSRF_CLAIM_NAME;

    // remove unwanted keys
    for( let key in options ) {

        if( options[ key ] === undefined ) {

            delete options[ key ];
        }
    }

    // configure if we've got something
    if( Object.keys( options ).length > 0 ) {

        instance.updateConfiguration( options );
    }
}

class JWTPlugin extends Plugin {

    constructor() {

        super( 'jwt' );

        this.configuration = new JWTConfiguration();

        configureFromEnvVars( this );
    }

    configure( config ) {

        let options = config.jwt;

        this.updateConfiguration( options );
    }

    updateConfiguration( options ) {

        if( options ) {

            this.configuration.update( options );
        }
    }

    enable() {

        this.configure( {} );
    }

    isEnabled() {

        return this.configuration.isEnabled();
    }

    execute( pipelineEvent, callback ) {

        try {

            validator.validate( pipelineEvent.event, this.configuration );

            let keysToIgnore = this.configuration.getIgnoredProperties( pipelineEvent.event );

            if( keysToIgnore ) {

                for( let item of keysToIgnore ) {

                    pipelineEvent.ignored.push( item );
                }
            }

            callback();
        }
        catch( err ) {

            callback( err );
        }
    }

    get state() {

        return this.configuration.state();
    }
}

module.exports = JWTPlugin;
<commit_msg>Improved configuration<commit_after>'use strict';

const utils = require( '../../utils' );

const JWTConfiguration = require( './configuration' );

const validator = require( './validator' );

const Plugin = require( '../plugin' );

function configureFromEnvVars( instance ) {

    let options = {};

    options.algorithm = process.env.VANDIUM_JWT_ALGORITHM;
    options.secret = process.env.VANDIUM_JWT_SECRET;
    options.public_key = process.env.VANDIUM_JWT_PUBKEY;
    options.token_name = process.env.VANDIUM_JWT_TOKEN_NAME;

    if( process.env.VANDIUM_JWT_USE_XSRF ) {

        options.xsrf = utils.parseBoolean( process.env.VANDIUM_JWT_USE_XSRF );
    }

    options.xsrf_token_name = process.env.VANDIUM_JWT_XSRF_TOKEN_NAME;
    options.xsrf_claim_name = process.env.VANDIUM_JWT_XSRF_CLAIM_NAME;

    // remove unwanted keys
    for( let key in options ) {

        if( options[ key ] === undefined ) {

            delete options[ key ];
        }
    }

    instance.configure( options );
}

class JWTPlugin extends Plugin {

    constructor() {

        super( 'jwt' );

        this.configuration = new JWTConfiguration();

        configureFromEnvVars( this );
    }

    configure( config ) {

        this.configuration.update( config );
    }

    enable() {

        this.configure( {} );
    }

    isEnabled() {

        return this.configuration.isEnabled();
    }

    execute( pipelineEvent, callback ) {

        try {

            validator.validate( pipelineEvent.event, this.configuration );

            let keysToIgnore = this.configuration.getIgnoredProperties( pipelineEvent.event );

            if( keysToIgnore ) {

                for( let item of keysToIgnore ) {

                    pipelineEvent.ignored.push( item );
                }
            }

            callback();
        }
        catch( err ) {

            callback( err );
        }
    }

    get state() {

        return this.configuration.state;
    }
}

module.exports = JWTPlugin;
<|endoftext|>


<commit_before>// Import React
import React from "react";

// Import Spectacle Core tags
import {
  Appear,
  BlockQuote,
  Cite,
  CodePane,
  Deck,
  Fill,
  Heading,
  Image,
  Layout,
  Link,
  ListItem,
  List,
  Markdown,
  Quote,
  Slide,
  Spectacle,
  Text
} from "spectacle";

// Import image preloader util
import preloader from "spectacle/lib/utils/preloader";

// Import theme
import createTheme from "spectacle/lib/themes/default";

// Import custom component
import Interactive from "../assets/interactive";

// Require CSS
require("normalize.css");
require("spectacle/lib/themes/default/index.css");

const images = {
  logoWina: require('../assets/logo-winamax.png'),
  avatarJdn: require('../assets/avatar-jdn.jpg'),
  avatarMlk: require('../assets/avatar-mlk.jpg'),
  difficult: require('../assets/difficult.png'),
  dontBelieve: require('../assets/dontbelieve.gif'),
  scuTab: require('../assets/scu-tableau.png'),
  scuBad: require('../assets/scu-bad.png'),
  scuGood: require('../assets/scu-good.png'),
  beamMeUp: require('../assets/keep-calm-and-beam-me-up-scotty-19.png'),
  reduxLogo: require('../assets/redux-logo.png'),
  reduxCycle: require('../assets/redux-cycle.svg'),
  virtualDom: require('../assets/virtual-dom-update.png'),
  flux: require('../assets/flux.png'),
  coke: require('../assets/coke.jpg'),
  mentos: require('../assets/mentos.jpg'),
  cokementos: require('../assets/mentoscoke.gif'),
  reduxMiddleware: require('../assets/middleware.png'),
  catConquer: require('../assets/cat-conquer.jpg'),
  reactNative: require('../assets/reactnative.png'),

  city: require("../assets/city.jpg"),
  kat: require("../assets/kat.png"),
  logo: require("../assets/formidable-logo.svg"),
  markdown: require("../assets/markdown.png")
};

preloader(images);

const theme = createTheme({
  primary: "#F0F0F0",
  secondary: "black",
  quartenary: "black"
});

export default class Presentation extends React.Component {
  render() {
    return (
      <Spectacle theme={theme}>
        <Deck transition={["slide"]} transitionDuration={500}>
          <Slide transition={["zoom"]} bgColor="primary">
          <Image src={images.logoWina.replace("/", "")} margin="0px auto 40px" height="121px"/>
            <Heading size={1} fit caps lineHeight={2} textColor="black">
              ReactJS / Redux
            </Heading>
            <Heading size={1} fit caps textColor="secondary">
              Découverte et mise en œuvre @ Winamax
            </Heading>

          </Slide>
          <Slide>
            <Heading textColor="secondary">Qui sommes nous ?</Heading>
            <Layout>
              <Fill>
                Julien Déléan

@Chabou69

<Image src={images.avatarJdn.replace("/", "")} height="150px"/>

Développements applicatifs clients
              </Fill>
              <Fill>
                Mathieu Lesniak

@mathieulesniak

<Image src={images.avatarMlk.replace("/", "")} height="150px"/>

Développements frontend & betting
              </Fill>
            </Layout>
          </Slide>

          <Slide>
            <Heading textColor="secondary" caps>Timeline</Heading>
            <List>
              <ListItem>Avril 2012 rachat d'Instagram.com</ListItem>
              <ListItem>Refonte / intégration au sein de Facebook</ListItem>
              <ListItem>Juin 2013, open source React</ListItem>
              <ListItem>Utilisation interne à FB (messenger)</ListItem>
            </List>
          </Slide>
          <Slide>
            <Heading textColor="secondary">Qui l'utilise ?</Heading>
            <List>
              <ListItem>Apple (developer portal)</ListItem>
              <ListItem>Wallmart</ListItem>
              <ListItem>AirBNB</ListItem>
              <ListItem>Netflix</ListItem>
              <ListItem>BBC (module de recherche)</ListItem>
              <ListItem>Yahoo Mail</ListItem>
              <ListItem>Reddit Mobile</ListItem>
              <ListItem>Dropbox, Facebook, Github, IG, Wired</ListItem>
              <ListItem>... et Winamax !</ListItem>
            </List>
          </Slide>

          <Slide>
            <Heading textColor="secondary" fit>React ne fait que le V de MVC</Heading>
            <Appear>
              <Heading textColor="secondary" size={2} fit>React n'a pas de :</Heading>
            </Appear>
            <Appear>
              <List>
                <ListItem>Controllers</ListItem>
                <ListItem>Directive</ListItem>
                <ListItem>Templates</ListItem>
                <ListItem>Global event listener</ListItem>
              </List>
            </Appear>
            <Appear>
              <Heading textColor="secondary" size={3}>Seulement... des composants</Heading>
            </Appear>
          </Slide>

          <Slide>
            <Heading textColor="secondary" fit>Et donc, c'est difficile ?</Heading>
            <Image src={images.difficult.replace('/', '')} />
          </Slide>

          <Slide>
            <CodePane
              lang="jsx"
              source={require("raw!../assets/jsx.example")}
              margin="20px auto"
            />
            <Appear>
              <Heading textColor="secondary" size={3}>Une seule méthode obligatoire : render()</Heading>
            </Appear>
          </Slide>

          <Slide>
            <Heading textColor="secondary" fit size={2}>Faites des composants, pas des templates,</Heading>
            <Heading textColor="secondary" fit size={2}>Encapsulez-les et réutilisez-les</Heading>
          </Slide>

          <Slide>
            <CodePane
              lang="jsx"
              source={require("raw!../assets/jsx.example")}
              margin="20px auto"
            />
            <CodePane
              lang="jsx"
              source={require("raw!../assets/jsx2.example")}
              margin="20px auto"
            />
            <CodePane
              lang="jsx"
              source={require("raw!../assets/jsx-composed.example")}
              margin="20px auto"
            />
          </Slide>

          <Slide>
            <Heading textColor="secondary" fit>Le passage de variable ?</Heading>
            <Heading textColor="secondary" fit>Une solution : les props</Heading>
          </Slide>

          <Slide>
            <CodePane
              lang="jsx"
              source={require("raw!../assets/jsx-props1.example")}
              margin="20px auto"
            />
            <CodePane
              lang="jsx"
              source={require("raw!../assets/jsx-props2.example")}
              margin="20px auto"
            />
          </Slide>

          <Slide>
            <Heading textColor="black" fit>Les props sont "immutable"</Heading>
            <Heading textColor="black" fit>les composants enfants ne peuvent les modifier</Heading>
          </Slide>

          <Slide>
            <Heading textColor="secondary">Render() à chaque update</Heading>
          </Slide>

          <Slide>
            <Heading textColor="secondary" fit>On nous aurait donc menti ?</Heading>
            <Image src={images.dontBelieve.replace('/', '')} />
          </Slide>

          <Slide>
            <Heading textColor="secondary" fit>Rappel sur les best practices pour le DOM</Heading>
            <List>
              <Appear><ListItem>On n'effectue pas de grosses opérations sur le DOM</ListItem></Appear>
              <Appear><ListItem>On limite les accès au DOM</ListItem></Appear>
              <Appear><ListItem>On modifie des éléments avant de les ajouter au DOM</ListItem></Appear>
            </List>
          </Slide>

          <Slide>
            <Heading textColor="secondary" textColor="secondary">Virtual DOM</Heading>
            <List>
                <ListItem>DOM allégé (pour être belle en maillot cet été)</ListItem>
                <ListItem>Diff entre la nouvelle version et l'ancienne</ListItem>
                <ListItem>Calcul des modifications à apporter au véritable DOM</ListItem>
                <ListItem>Application en batch des modifications</ListItem>
              </List>
          </Slide>

          <Slide>
            <Image src={images.virtualDom.replace('/', '')} />
          </Slide>

          <Slide>
            <Heading textColor="secondary" fit>Cycle de vie des composants</Heading>
            <List>
              <ListItem>componentWillMount</ListItem>
              <ListItem>componentDidMount</ListItem>
              <ListItem>componentWillReceiveProps</ListItem>
              <ListItem>shouldComponentUpdate</ListItem>
              <ListItem>componentWillUpdate</ListItem>
            </List>
          </Slide>

          <Slide>
            <Heading textColor="secondary" fit>shouldComponentUpdate()</Heading>
            <Image src={images.scuTab.replace('/', '')} />
            <Layout>
              <Fill>
                <Text>Avant</Text>
                <Image src={images.scuBad.replace('/', '')} />
              </Fill>
              <Fill>
                <Text>Après</Text>
                <Image src={images.scuGood.replace('/', '')} />
              </Fill>
            </Layout>
          </Slide>

          <Slide>
            <Heading textColor="secondary" size={4} fit>Donc, nous avons un modèle descendant</Heading>
            <Image src={images.beamMeUp.replace('/', '')} width="40%"/>
            <Heading size={4} textColor="secondary" fit >Mais comment fait-on pour remonter ?</Heading>
          </Slide>

          <Slide transition={["slide"]}>
            <Heading textColor="secondary" textColor="secondary">Flux Pattern</Heading>
            <Text>(Unidirectionnal Data Flow)</Text>
            <Appear><Image src={images.flux.replace('/', '')} width="100%"/></Appear>
            <List>
              <Appear><ListItem>Pas de librairie officielle, uniquement le Dispatcher</ListItem></Appear>
              <Appear><ListItem><Text>Flummox, Alt, Reflux, MartyJS, Delorean, Fluxxor...</Text></ListItem></Appear>
            </List>
          </Slide>

          <Slide>
            <Image src={images.reduxLogo.replace('/', '')} width="40%"/>
            <List>
              <ListItem>Expérimentation de Dan Abramov (ReactEurope 2015)</ListItem>
              <Appear><ListItem>Inspiration d'ELM (langage purement fonctionnel)</ListItem></Appear>
              <Appear><ListItem>L'objectif ?</ListItem></Appear>
            </List>
            <Layout fit>
              <Appear><Fill>
                <Image src={images.coke.replace('/','')} width="50%"/>
                <Text>Mutabilité</Text>
              </Fill></Appear>
              <Appear><Fill>
                <Image src={images.mentos.replace('/','')} width="50%"/>
                <Text>Asynchronisme</Text>
              </Fill></Appear>
            </Layout>
          </Slide>

          <Slide>
            <Image src={images.cokementos.replace('/','')} width="50%"/>
            <Text>Mutablité + Asynchronisme</Text>
          </Slide>

          <Slide>
            <Heading textColor="secondary" size={4}>3 principes</Heading>
            <Layout fit>
              <Appear><Fill>
                Single source of truth: State
                <CodePane
                  lang="js"
                  source={require("raw!../assets/state.example")}
                />
              </Fill></Appear>
              <Appear><Fill>
                State read-only: Actions
                <CodePane
                  lang="js"
                  source={require("raw!../assets/actions.example")}
                />
              </Fill></Appear>
              <Appear><Fill>
                Pure functions: Reducers
                <CodePane
                  lang="js"
                  source={require("raw!../assets/reducers.example")}
                />
              </Fill></Appear>
            </Layout>
          </Slide>

          <Slide>
            <Image src={images.reduxMiddleware.replace('/', '')} />
            <Text>source: visual-engin.com</Text>
          </Slide>

          <Slide>
            <Heading textColor="secondary">Et demain ?</Heading>
            <Image src={images.catConquer.replace('/', '')} />

          </Slide>

          <Slide>
            <Heading textColor="secondary">En avant vers le mobile :</Heading>
            <Image src={images.reactNative.replace('/', '')} />
          </Slide>

          <Slide>
            <Image src={images.logoWina.replace("/", "")} margin="0px auto 40px" height="121px"/>
            <Heading textColor="secondary">Merci !</Heading>
            <Text>Venez nous voir, chez Winamax, nous adorons ReactJS, et nous avons pleins de projets sympas à réaliser</Text>
            <Text>
PS : cette présentation a été réalisée sous ReactJS ;)</Text>
          </Slide>

          <Slide transition={["slide"]} bgColor="black" notes="You can even put notes on your slide. How awesome is that?">
            <Image src={images.kat.replace("/", "")} margin="0px auto 40px" height="293px"/>
            <Heading size={2} caps fit textColor="primary" textFont="primary">
              Wait what?
            </Heading>
          </Slide>
          <Slide transition={["zoom", "fade"]} bgColor="primary" notes="

talk about that

and that

">
            <CodePane
              lang="jsx"
              source={require("raw!../assets/deck.example")}
              margin="20px auto"
            />
          </Slide>
          <Slide transition={["slide"]} bgImage={images.city.replace("/", "")} bgDarken={0.75}>
            <Appear fid="1">
              <Heading size={1} caps fit textColor="primary">
                Full Width
              </Heading>
            </Appear>
            <Appear fid="2">
              <Heading size={1} caps fit textColor="tertiary">
                Adjustable Darkness
              </Heading>
            </Appear>
            <Appear fid="3">
              <Heading size={1} caps fit textColor="primary">
                Background Imagery
              </Heading>
            </Appear>
          </Slide>
          <Slide transition={["zoom", "fade"]} bgColor="primary">
            <Heading caps fit>Flexible Layouts</Heading>
            <Layout>
              <Fill>
                <Heading size={4} caps textColor="secondary" bgColor="white" margin={10}>
                  Left
                </Heading>
              </Fill>
              <Fill>
                <Heading size={4} caps textColor="secondary" bgColor="white" margin={10}>
                  Right
                </Heading>
              </Fill>
            </Layout>
          </Slide>
          <Slide transition={["slide"]} bgColor="black">

              <Quote>Wonderfully formatted quotes</Quote>
              Ken Wheeler

          </Slide>
          <Slide transition={["spin", "zoom"]} bgColor="tertiary">
            <Heading caps fit size={1} textColor="primary">
              Inline Markdown
            </Heading>
            <Markdown>
              {`
![Markdown Logo](${images.markdown.replace("/", "")})

You can write inline images, [Markdown Links](http://commonmark.org), paragraph text and most other markdown syntax
* Lists too!
* With ~~strikethrough~~ and _italic_
* And lets not forget **bold**
              `}
            </Markdown>
          </Slide>
          <Slide transition={["slide", "spin"]} bgColor="primary">
            <Heading caps fit size={1} textColor="tertiary">
              Smooth
            </Heading>
            <Heading caps fit size={1} textColor="secondary">
              Combinable Transitions
            </Heading>
          </Slide>
          <Slide transition={["fade"]} bgColor="secondary" textColor="primary">
            <List>
              <Appear><ListItem>Inline style based theme system</ListItem></Appear>
              <Appear><ListItem>Autofit text</ListItem></Appear>
              <Appear><ListItem>Flexbox layout system</ListItem></Appear>
              <Appear><ListItem>React-Router navigation</ListItem></Appear>
              <Appear><ListItem>PDF export</ListItem></Appear>
              <Appear><ListItem>And...</ListItem></Appear>
            </List>
          </Slide>
          <Slide transition={["slide"]} bgColor="primary">
            <Heading size={1} caps fit textColor="tertiary">
              Your presentations are interactive
            </Heading>
            <Interactive/>
          </Slide>
          <Slide transition={["spin", "slide"]} bgColor="tertiary">
            <Heading size={1} caps fit lineHeight={1.5} textColor="primary">
              Made with love in Seattle by
            </Heading>
            <Link href="http://www.formidablelabs.com"><Image width="100%" src={images.logo}/></Link>
          </Slide>
        </Deck>
      </Spectacle>
    );
  }
}
<commit_msg>Remove example slides<commit_after>// Import React
import React from "react";

// Import Spectacle Core tags
import {
  Appear,
  BlockQuote,
  Cite,
  CodePane,
  Deck,
  Fill,
  Heading,
  Image,
  Layout,
  Link,
  ListItem,
  List,
  Markdown,
  Quote,
  Slide,
  Spectacle,
  Text
} from "spectacle";

// Import image preloader util
import preloader from "spectacle/lib/utils/preloader";

// Import theme
import createTheme from "spectacle/lib/themes/default";

// Import custom component
import Interactive from "../assets/interactive";

// Require CSS
require("normalize.css");
require("spectacle/lib/themes/default/index.css");

const images = {
  logoWina: require('../assets/logo-winamax.png'),
  avatarJdn: require('../assets/avatar-jdn.jpg'),
  avatarMlk: require('../assets/avatar-mlk.jpg'),
  difficult: require('../assets/difficult.png'),
  dontBelieve: require('../assets/dontbelieve.gif'),
  scuTab: require('../assets/scu-tableau.png'),
  scuBad: require('../assets/scu-bad.png'),
  scuGood: require('../assets/scu-good.png'),
  beamMeUp: require('../assets/keep-calm-and-beam-me-up-scotty-19.png'),
  reduxLogo: require('../assets/redux-logo.png'),
  reduxCycle: require('../assets/redux-cycle.svg'),
  virtualDom: require('../assets/virtual-dom-update.png'),
  flux: require('../assets/flux.png'),
  coke: require('../assets/coke.jpg'),
  mentos: require('../assets/mentos.jpg'),
  cokementos: require('../assets/mentoscoke.gif'),
  reduxMiddleware: require('../assets/middleware.png'),
  catConquer: require('../assets/cat-conquer.jpg'),
  reactNative: require('../assets/reactnative.png'),

  city: require("../assets/city.jpg"),
  kat: require("../assets/kat.png"),
  logo: require("../assets/formidable-logo.svg"),
  markdown: require("../assets/markdown.png")
};

preloader(images);

const theme = createTheme({
  primary: "#F0F0F0",
  secondary: "black",
  quartenary: "black"
});

export default class Presentation extends React.Component {
  render() {
    return (
      <Spectacle theme={theme}>
        <Deck transition={["slide"]} transitionDuration={500}>
          <Slide transition={["zoom"]} bgColor="primary">
          <Image src={images.logoWina.replace("/", "")} margin="0px auto 40px" height="121px"/>
            <Heading size={1} fit caps lineHeight={2} textColor="black">
              ReactJS / Redux
            </Heading>
            <Heading size={1} fit caps textColor="secondary">
              Découverte et mise en œuvre @ Winamax
            </Heading>

          </Slide>
          <Slide>
            <Heading textColor="secondary">Qui sommes nous ?</Heading>
            <Layout>
              <Fill>
                Julien Déléan

@Chabou69

<Image src={images.avatarJdn.replace("/", "")} height="150px"/>

Développements applicatifs clients
              </Fill>
              <Fill>
                Mathieu Lesniak

@mathieulesniak

<Image src={images.avatarMlk.replace("/", "")} height="150px"/>

Développements frontend & betting
              </Fill>
            </Layout>
          </Slide>

          <Slide>
            <Heading textColor="secondary" caps>Timeline</Heading>
            <List>
              <ListItem>Avril 2012 rachat d'Instagram.com</ListItem>
              <ListItem>Refonte / intégration au sein de Facebook</ListItem>
              <ListItem>Juin 2013, open source React</ListItem>
              <ListItem>Utilisation interne à FB (messenger)</ListItem>
            </List>
          </Slide>
          <Slide>
            <Heading textColor="secondary">Qui l'utilise ?</Heading>
            <List>
              <ListItem>Apple (developer portal)</ListItem>
              <ListItem>Wallmart</ListItem>
              <ListItem>AirBNB</ListItem>
              <ListItem>Netflix</ListItem>
              <ListItem>BBC (module de recherche)</ListItem>
              <ListItem>Yahoo Mail</ListItem>
              <ListItem>Reddit Mobile</ListItem>
              <ListItem>Dropbox, Facebook, Github, IG, Wired</ListItem>
              <ListItem>... et Winamax !</ListItem>
            </List>
          </Slide>

          <Slide>
            <Heading textColor="secondary" fit>React ne fait que le V de MVC</Heading>
            <Appear>
              <Heading textColor="secondary" size={2} fit>React n'a pas de :</Heading>
            </Appear>
            <Appear>
              <List>
                <ListItem>Controllers</ListItem>
                <ListItem>Directive</ListItem>
                <ListItem>Templates</ListItem>
                <ListItem>Global event listener</ListItem>
              </List>
            </Appear>
            <Appear>
              <Heading textColor="secondary" size={3}>Seulement... des composants</Heading>
            </Appear>
          </Slide>

          <Slide>
            <Heading textColor="secondary" fit>Et donc, c'est difficile ?</Heading>
            <Image src={images.difficult.replace('/', '')} />
          </Slide>

          <Slide>
            <CodePane
              lang="jsx"
              source={require("raw!../assets/jsx.example")}
              margin="20px auto"
            />
            <Appear>
              <Heading textColor="secondary" size={3}>Une seule méthode obligatoire : render()</Heading>
            </Appear>
          </Slide>

          <Slide>
            <Heading textColor="secondary" fit size={2}>Faites des composants, pas des templates,</Heading>
            <Heading textColor="secondary" fit size={2}>Encapsulez-les et réutilisez-les</Heading>
          </Slide>

          <Slide>
            <CodePane
              lang="jsx"
              source={require("raw!../assets/jsx.example")}
              margin="20px auto"
            />
            <CodePane
              lang="jsx"
              source={require("raw!../assets/jsx2.example")}
              margin="20px auto"
            />
            <CodePane
              lang="jsx"
              source={require("raw!../assets/jsx-composed.example")}
              margin="20px auto"
            />
          </Slide>

          <Slide>
            <Heading textColor="secondary" fit>Le passage de variable ?</Heading>
            <Heading textColor="secondary" fit>Une solution : les props</Heading>
          </Slide>

          <Slide>
            <CodePane
              lang="jsx"
              source={require("raw!../assets/jsx-props1.example")}
              margin="20px auto"
            />
            <CodePane
              lang="jsx"
              source={require("raw!../assets/jsx-props2.example")}
              margin="20px auto"
            />
          </Slide>

          <Slide>
            <Heading textColor="black" fit>Les props sont "immutable"</Heading>
            <Heading textColor="black" fit>les composants enfants ne peuvent les modifier</Heading>
          </Slide>

          <Slide>
            <Heading textColor="secondary">Render() à chaque update</Heading>
          </Slide>

          <Slide>
            <Heading textColor="secondary" fit>On nous aurait donc menti ?</Heading>
            <Image src={images.dontBelieve.replace('/', '')} />
          </Slide>

          <Slide>
            <Heading textColor="secondary" fit>Rappel sur les best practices pour le DOM</Heading>
            <List>
              <Appear><ListItem>On n'effectue pas de grosses opérations sur le DOM</ListItem></Appear>
              <Appear><ListItem>On limite les accès au DOM</ListItem></Appear>
              <Appear><ListItem>On modifie des éléments avant de les ajouter au DOM</ListItem></Appear>
            </List>
          </Slide>

          <Slide>
            <Heading textColor="secondary" textColor="secondary">Virtual DOM</Heading>
            <List>
                <ListItem>DOM allégé (pour être belle en maillot cet été)</ListItem>
                <ListItem>Diff entre la nouvelle version et l'ancienne</ListItem>
                <ListItem>Calcul des modifications à apporter au véritable DOM</ListItem>
                <ListItem>Application en batch des modifications</ListItem>
              </List>
          </Slide>

          <Slide>
            <Image src={images.virtualDom.replace('/', '')} />
          </Slide>

          <Slide>
            <Heading textColor="secondary" fit>Cycle de vie des composants</Heading>
            <List>
              <ListItem>componentWillMount</ListItem>
              <ListItem>componentDidMount</ListItem>
              <ListItem>componentWillReceiveProps</ListItem>
              <ListItem>shouldComponentUpdate</ListItem>
              <ListItem>componentWillUpdate</ListItem>
            </List>
          </Slide>

          <Slide>
            <Heading textColor="secondary" fit>shouldComponentUpdate()</Heading>
            <Image src={images.scuTab.replace('/', '')} />
            <Layout>
              <Fill>
                <Text>Avant</Text>
                <Image src={images.scuBad.replace('/', '')} />
              </Fill>
              <Fill>
                <Text>Après</Text>
                <Image src={images.scuGood.replace('/', '')} />
              </Fill>
            </Layout>
          </Slide>

          <Slide>
            <Heading textColor="secondary" size={4} fit>Donc, nous avons un modèle descendant</Heading>
            <Image src={images.beamMeUp.replace('/', '')} width="40%"/>
            <Heading size={4} textColor="secondary" fit >Mais comment fait-on pour remonter ?</Heading>
          </Slide>

          <Slide transition={["slide"]}>
            <Heading textColor="secondary" textColor="secondary">Flux Pattern</Heading>
            <Text>(Unidirectionnal Data Flow)</Text>
            <Appear><Image src={images.flux.replace('/', '')} width="100%"/></Appear>
            <List>
              <Appear><ListItem>Pas de librairie officielle, uniquement le Dispatcher</ListItem></Appear>
              <Appear><ListItem><Text>Flummox, Alt, Reflux, MartyJS, Delorean, Fluxxor...</Text></ListItem></Appear>
            </List>
          </Slide>

          <Slide>
            <Image src={images.reduxLogo.replace('/', '')} width="40%"/>
            <List>
              <ListItem>Expérimentation de Dan Abramov (ReactEurope 2015)</ListItem>
              <Appear><ListItem>Inspiration d'ELM (langage purement fonctionnel)</ListItem></Appear>
              <Appear><ListItem>L'objectif ?</ListItem></Appear>
            </List>
            <Layout fit>
              <Appear><Fill>
                <Image src={images.coke.replace('/','')} width="50%"/>
                <Text>Mutabilité</Text>
              </Fill></Appear>
              <Appear><Fill>
                <Image src={images.mentos.replace('/','')} width="50%"/>
                <Text>Asynchronisme</Text>
              </Fill></Appear>
            </Layout>
          </Slide>

          <Slide>
            <Image src={images.cokementos.replace('/','')} width="50%"/>
            <Text>Mutablité + Asynchronisme</Text>
          </Slide>

          <Slide>
            <Heading textColor="secondary" size={4}>3 principes</Heading>
            <Layout fit>
              <Appear><Fill>
                Single source of truth: State
                <CodePane
                  lang="js"
                  source={require("raw!../assets/state.example")}
                />
              </Fill></Appear>
              <Appear><Fill>
                State read-only: Actions
                <CodePane
                  lang="js"
                  source={require("raw!../assets/actions.example")}
                />
              </Fill></Appear>
              <Appear><Fill>
                Pure functions: Reducers
                <CodePane
                  lang="js"
                  source={require("raw!../assets/reducers.example")}
                />
              </Fill></Appear>
            </Layout>
          </Slide>

          <Slide>
            <Image src={images.reduxMiddleware.replace('/', '')} />
            <Text>source: visual-engin.com</Text>
          </Slide>

          <Slide>
            <Heading textColor="secondary">Et demain ?</Heading>
            <Image src={images.catConquer.replace('/', '')} />

          </Slide>

          <Slide>
            <Heading textColor="secondary">En avant vers le mobile :</Heading>
            <Image src={images.reactNative.replace('/', '')} />
          </Slide>

          <Slide>
            <Image src={images.logoWina.replace("/", "")} margin="0px auto 40px" height="121px"/>
            <Heading textColor="secondary">Merci !</Heading>
            <Text>Venez nous voir, chez Winamax, nous adorons ReactJS, et nous avons pleins de projets sympas à réaliser</Text>
            <Text>
PS : cette présentation a été réalisée sous ReactJS ;)</Text>
          </Slide>

        </Deck>
      </Spectacle>
    );
  }
}
<|endoftext|>