├── .env ├── .env.test ├── .gitignore ├── .sequelizerc ├── __tests__ ├── coverage │ ├── clover.xml │ ├── coverage-final.json │ ├── lcov-report │ │ ├── base.css │ │ ├── block-navigation.js │ │ ├── index.html │ │ ├── prettify.css │ │ ├── prettify.js │ │ ├── sort-arrow-sprite.png │ │ ├── sorter.js │ │ └── src │ │ │ ├── app.js.html │ │ │ ├── app │ │ │ ├── controllers │ │ │ │ ├── SessionController.js.html │ │ │ │ └── index.html │ │ │ ├── middleware │ │ │ │ ├── auth.js.html │ │ │ │ └── index.html │ │ │ └── models │ │ │ │ ├── User.js.html │ │ │ │ ├── index.html │ │ │ │ └── index.js.html │ │ │ ├── config │ │ │ ├── database.js.html │ │ │ └── index.html │ │ │ ├── database │ │ │ └── migrations │ │ │ │ ├── 20190219195005-create-users.js.html │ │ │ │ └── index.html │ │ │ ├── index.html │ │ │ ├── routes.js.html │ │ │ └── server.js.html │ └── lcov.info ├── database.sqlite ├── factories.js ├── integration │ └── session.test.js ├── unit │ └── user.test.js └── utils │ └── truncate.js ├── jest.config.js ├── package.json ├── src ├── app.js ├── app │ ├── controllers │ │ └── SessionController.js │ ├── middleware │ │ └── auth.js │ └── models │ │ ├── User.js │ │ └── index.js ├── config │ └── database.js ├── database │ └── migrations │ │ └── 20190219195005-create-users.js ├── routes.js └── server.js └── yarn.lock /.env: -------------------------------------------------------------------------------- 1 | APP_SECRET=authnodetdd 2 | 3 | DB_HOST=127.0.0.1 4 | DB_USER=docker 5 | DB_PASS=docker 6 | DB_NAME=nodeauth -------------------------------------------------------------------------------- /.env.test: -------------------------------------------------------------------------------- 1 | APP_SECRET=authnodetddtest 2 | 3 | DB_DIALECT=sqlite -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /.sequelizerc: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | 3 | module.exports = { 4 | config: path.resolve('src', 'config', 'database.js'), 5 | 'models-path': path.resolve('src', 'app', 'models'), 6 | 'seeders-path': path.resolve('src', 'database', 'seeders'), 7 | 'migrations-path': path.resolve('src', 'database', 'migrations'), 8 | }; -------------------------------------------------------------------------------- /__tests__/coverage/clover.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | -------------------------------------------------------------------------------- /__tests__/coverage/coverage-final.json: -------------------------------------------------------------------------------- 1 | {"/Users/diegofernandes/www/masterclass/authtdd/src/app.js": {"path":"/Users/diegofernandes/www/masterclass/authtdd/src/app.js","statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":3,"column":3}},"1":{"start":{"line":5,"column":16},"end":{"line":5,"column":34}},"2":{"start":{"line":9,"column":4},"end":{"line":9,"column":29}},"3":{"start":{"line":11,"column":4},"end":{"line":11,"column":23}},"4":{"start":{"line":12,"column":4},"end":{"line":12,"column":18}},"5":{"start":{"line":16,"column":4},"end":{"line":16,"column":37}},"6":{"start":{"line":20,"column":4},"end":{"line":20,"column":42}},"7":{"start":{"line":24,"column":0},"end":{"line":24,"column":45}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":8,"column":2},"end":{"line":8,"column":3}},"loc":{"start":{"line":8,"column":16},"end":{"line":13,"column":3}},"line":8},"1":{"name":"(anonymous_1)","decl":{"start":{"line":15,"column":2},"end":{"line":15,"column":3}},"loc":{"start":{"line":15,"column":16},"end":{"line":17,"column":3}},"line":15},"2":{"name":"(anonymous_2)","decl":{"start":{"line":19,"column":2},"end":{"line":19,"column":3}},"loc":{"start":{"line":19,"column":11},"end":{"line":21,"column":3}},"line":19}},"branchMap":{"0":{"loc":{"start":{"line":2,"column":8},"end":{"line":2,"column":62}},"type":"cond-expr","locations":[{"start":{"line":2,"column":42},"end":{"line":2,"column":53}},{"start":{"line":2,"column":56},"end":{"line":2,"column":62}}],"line":2}},"s":{"0":1,"1":1,"2":1,"3":1,"4":1,"5":1,"6":1,"7":1},"f":{"0":1,"1":1,"2":1},"b":{"0":[1,0]},"_coverageSchema":"43e27e138ebf9cfc5966b082cf9a028302ed4184","hash":"b2bd903eb17e3b5c26eea0d5d5e849db3abf8cd2"} 2 | ,"/Users/diegofernandes/www/masterclass/authtdd/src/routes.js": {"path":"/Users/diegofernandes/www/masterclass/authtdd/src/routes.js","statementMap":{"0":{"start":{"line":1,"column":15},"end":{"line":1,"column":42}},"1":{"start":{"line":3,"column":23},"end":{"line":3,"column":55}},"2":{"start":{"line":5,"column":26},"end":{"line":5,"column":72}},"3":{"start":{"line":7,"column":0},"end":{"line":7,"column":50}},"4":{"start":{"line":9,"column":0},"end":{"line":9,"column":27}},"5":{"start":{"line":11,"column":0},"end":{"line":13,"column":3}},"6":{"start":{"line":12,"column":2},"end":{"line":12,"column":32}},"7":{"start":{"line":15,"column":0},"end":{"line":15,"column":24}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":11,"column":25},"end":{"line":11,"column":26}},"loc":{"start":{"line":11,"column":39},"end":{"line":13,"column":1}},"line":11}},"branchMap":{},"s":{"0":1,"1":1,"2":1,"3":1,"4":1,"5":1,"6":1,"7":1},"f":{"0":1},"b":{},"_coverageSchema":"43e27e138ebf9cfc5966b082cf9a028302ed4184","hash":"b9db8b048d9e36ac13a25129eb84357b53f91b28"} 3 | ,"/Users/diegofernandes/www/masterclass/authtdd/src/server.js": {"path":"/Users/diegofernandes/www/masterclass/authtdd/src/server.js","statementMap":{"0":{"start":{"line":1,"column":12},"end":{"line":1,"column":28}},"1":{"start":{"line":3,"column":0},"end":{"line":3,"column":37}}},"fnMap":{},"branchMap":{"0":{"loc":{"start":{"line":3,"column":11},"end":{"line":3,"column":35}},"type":"binary-expr","locations":[{"start":{"line":3,"column":11},"end":{"line":3,"column":27}},{"start":{"line":3,"column":31},"end":{"line":3,"column":35}}],"line":3}},"s":{"0":0,"1":0},"f":{},"b":{"0":[0,0]}} 4 | ,"/Users/diegofernandes/www/masterclass/authtdd/src/app/controllers/SessionController.js": {"path":"/Users/diegofernandes/www/masterclass/authtdd/src/app/controllers/SessionController.js","statementMap":{"0":{"start":{"line":1,"column":17},"end":{"line":1,"column":37}},"1":{"start":{"line":5,"column":32},"end":{"line":5,"column":40}},"2":{"start":{"line":7,"column":17},"end":{"line":7,"column":57}},"3":{"start":{"line":9,"column":4},"end":{"line":11,"column":5}},"4":{"start":{"line":10,"column":6},"end":{"line":10,"column":65}},"5":{"start":{"line":13,"column":4},"end":{"line":15,"column":5}},"6":{"start":{"line":14,"column":6},"end":{"line":14,"column":69}},"7":{"start":{"line":17,"column":4},"end":{"line":20,"column":7}},"8":{"start":{"line":24,"column":0},"end":{"line":24,"column":41}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":4,"column":2},"end":{"line":4,"column":3}},"loc":{"start":{"line":4,"column":24},"end":{"line":21,"column":3}},"line":4}},"branchMap":{"0":{"loc":{"start":{"line":9,"column":4},"end":{"line":11,"column":5}},"type":"if","locations":[{"start":{"line":9,"column":4},"end":{"line":11,"column":5}},{"start":{"line":9,"column":4},"end":{"line":11,"column":5}}],"line":9},"1":{"loc":{"start":{"line":13,"column":4},"end":{"line":15,"column":5}},"type":"if","locations":[{"start":{"line":13,"column":4},"end":{"line":15,"column":5}},{"start":{"line":13,"column":4},"end":{"line":15,"column":5}}],"line":13}},"s":{"0":1,"1":3,"2":3,"3":3,"4":0,"5":3,"6":1,"7":2,"8":1},"f":{"0":3},"b":{"0":[0,3],"1":[1,2]},"_coverageSchema":"43e27e138ebf9cfc5966b082cf9a028302ed4184","hash":"a2e9e9327ded66027be3554b81eb8353a0ad0108"} 5 | ,"/Users/diegofernandes/www/masterclass/authtdd/src/app/middleware/auth.js": {"path":"/Users/diegofernandes/www/masterclass/authtdd/src/app/middleware/auth.js","statementMap":{"0":{"start":{"line":1,"column":12},"end":{"line":1,"column":35}},"1":{"start":{"line":2,"column":22},"end":{"line":2,"column":37}},"2":{"start":{"line":4,"column":0},"end":{"line":22,"column":2}},"3":{"start":{"line":5,"column":21},"end":{"line":5,"column":46}},"4":{"start":{"line":7,"column":2},"end":{"line":9,"column":3}},"5":{"start":{"line":8,"column":4},"end":{"line":8,"column":67}},"6":{"start":{"line":11,"column":20},"end":{"line":11,"column":41}},"7":{"start":{"line":13,"column":2},"end":{"line":21,"column":3}},"8":{"start":{"line":14,"column":20},"end":{"line":14,"column":78}},"9":{"start":{"line":16,"column":4},"end":{"line":16,"column":28}},"10":{"start":{"line":18,"column":4},"end":{"line":18,"column":18}},"11":{"start":{"line":20,"column":4},"end":{"line":20,"column":62}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":4,"column":17},"end":{"line":4,"column":18}},"loc":{"start":{"line":4,"column":43},"end":{"line":22,"column":1}},"line":4}},"branchMap":{"0":{"loc":{"start":{"line":7,"column":2},"end":{"line":9,"column":3}},"type":"if","locations":[{"start":{"line":7,"column":2},"end":{"line":9,"column":3}},{"start":{"line":7,"column":2},"end":{"line":9,"column":3}}],"line":7}},"s":{"0":1,"1":1,"2":1,"3":3,"4":3,"5":1,"6":2,"7":2,"8":2,"9":1,"10":1,"11":1},"f":{"0":3},"b":{"0":[1,2]},"_coverageSchema":"43e27e138ebf9cfc5966b082cf9a028302ed4184","hash":"eda64897a5000f0edede8905e67d07571b86b9a6"} 6 | ,"/Users/diegofernandes/www/masterclass/authtdd/src/app/models/User.js": {"path":"/Users/diegofernandes/www/masterclass/authtdd/src/app/models/User.js","statementMap":{"0":{"start":{"line":1,"column":15},"end":{"line":1,"column":34}},"1":{"start":{"line":2,"column":12},"end":{"line":2,"column":35}},"2":{"start":{"line":4,"column":0},"end":{"line":33,"column":2}},"3":{"start":{"line":5,"column":15},"end":{"line":22,"column":3}},"4":{"start":{"line":16,"column":10},"end":{"line":18,"column":11}},"5":{"start":{"line":17,"column":12},"end":{"line":17,"column":69}},"6":{"start":{"line":24,"column":2},"end":{"line":26,"column":4}},"7":{"start":{"line":25,"column":4},"end":{"line":25,"column":56}},"8":{"start":{"line":28,"column":2},"end":{"line":30,"column":4}},"9":{"start":{"line":29,"column":4},"end":{"line":29,"column":61}},"10":{"start":{"line":32,"column":2},"end":{"line":32,"column":14}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":4,"column":17},"end":{"line":4,"column":18}},"loc":{"start":{"line":4,"column":43},"end":{"line":33,"column":1}},"line":4},"1":{"name":"(anonymous_1)","decl":{"start":{"line":15,"column":20},"end":{"line":15,"column":21}},"loc":{"start":{"line":15,"column":34},"end":{"line":19,"column":9}},"line":15},"2":{"name":"(anonymous_2)","decl":{"start":{"line":24,"column":33},"end":{"line":24,"column":34}},"loc":{"start":{"line":24,"column":52},"end":{"line":26,"column":3}},"line":24},"3":{"name":"(anonymous_3)","decl":{"start":{"line":28,"column":33},"end":{"line":28,"column":34}},"loc":{"start":{"line":28,"column":44},"end":{"line":30,"column":3}},"line":28}},"branchMap":{"0":{"loc":{"start":{"line":16,"column":10},"end":{"line":18,"column":11}},"type":"if","locations":[{"start":{"line":16,"column":10},"end":{"line":18,"column":11}},{"start":{"line":16,"column":10},"end":{"line":18,"column":11}}],"line":16}},"s":{"0":2,"1":2,"2":2,"3":2,"4":5,"5":5,"6":2,"7":3,"8":2,"9":3,"10":2},"f":{"0":2,"1":5,"2":3,"3":3},"b":{"0":[5,0]},"_coverageSchema":"43e27e138ebf9cfc5966b082cf9a028302ed4184","hash":"4877e60dba9a7f21e4504fb2f89df1d757fa913a"} 7 | ,"/Users/diegofernandes/www/masterclass/authtdd/src/app/models/index.js": {"path":"/Users/diegofernandes/www/masterclass/authtdd/src/app/models/index.js","statementMap":{"0":{"start":{"line":3,"column":11},"end":{"line":3,"column":24}},"1":{"start":{"line":4,"column":13},"end":{"line":4,"column":28}},"2":{"start":{"line":5,"column":18},"end":{"line":5,"column":38}},"3":{"start":{"line":6,"column":17},"end":{"line":6,"column":42}},"4":{"start":{"line":7,"column":15},"end":{"line":7,"column":47}},"5":{"start":{"line":8,"column":11},"end":{"line":8,"column":13}},"6":{"start":{"line":10,"column":18},"end":{"line":15,"column":1}},"7":{"start":{"line":17,"column":0},"end":{"line":26,"column":5}},"8":{"start":{"line":19,"column":4},"end":{"line":21,"column":6}},"9":{"start":{"line":24,"column":18},"end":{"line":24,"column":65}},"10":{"start":{"line":25,"column":4},"end":{"line":25,"column":27}},"11":{"start":{"line":28,"column":0},"end":{"line":32,"column":3}},"12":{"start":{"line":29,"column":2},"end":{"line":31,"column":3}},"13":{"start":{"line":30,"column":4},"end":{"line":30,"column":32}},"14":{"start":{"line":34,"column":0},"end":{"line":34,"column":25}},"15":{"start":{"line":35,"column":0},"end":{"line":35,"column":25}},"16":{"start":{"line":37,"column":0},"end":{"line":37,"column":20}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":18,"column":10},"end":{"line":18,"column":11}},"loc":{"start":{"line":18,"column":18},"end":{"line":22,"column":3}},"line":18},"1":{"name":"(anonymous_1)","decl":{"start":{"line":23,"column":11},"end":{"line":23,"column":12}},"loc":{"start":{"line":23,"column":19},"end":{"line":26,"column":3}},"line":23},"2":{"name":"(anonymous_2)","decl":{"start":{"line":28,"column":24},"end":{"line":28,"column":25}},"loc":{"start":{"line":28,"column":37},"end":{"line":32,"column":1}},"line":28}},"branchMap":{"0":{"loc":{"start":{"line":20,"column":6},"end":{"line":20,"column":78}},"type":"binary-expr","locations":[{"start":{"line":20,"column":6},"end":{"line":20,"column":29}},{"start":{"line":20,"column":33},"end":{"line":20,"column":50}},{"start":{"line":20,"column":54},"end":{"line":20,"column":78}}],"line":20},"1":{"loc":{"start":{"line":29,"column":2},"end":{"line":31,"column":3}},"type":"if","locations":[{"start":{"line":29,"column":2},"end":{"line":31,"column":3}},{"start":{"line":29,"column":2},"end":{"line":31,"column":3}}],"line":29}},"s":{"0":2,"1":2,"2":2,"3":2,"4":2,"5":2,"6":2,"7":2,"8":4,"9":2,"10":2,"11":2,"12":2,"13":0,"14":2,"15":2,"16":2},"f":{"0":4,"1":2,"2":2},"b":{"0":[4,4,2],"1":[0,2]},"_coverageSchema":"43e27e138ebf9cfc5966b082cf9a028302ed4184","hash":"65267b529f539d8908444f0ad29f1b6fa06fe435"} 8 | ,"/Users/diegofernandes/www/masterclass/authtdd/src/config/database.js": {"path":"/Users/diegofernandes/www/masterclass/authtdd/src/config/database.js","statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":3,"column":3}},"1":{"start":{"line":5,"column":0},"end":{"line":19,"column":2}}},"fnMap":{},"branchMap":{"0":{"loc":{"start":{"line":2,"column":8},"end":{"line":2,"column":62}},"type":"cond-expr","locations":[{"start":{"line":2,"column":42},"end":{"line":2,"column":53}},{"start":{"line":2,"column":56},"end":{"line":2,"column":62}}],"line":2},"1":{"loc":{"start":{"line":10,"column":11},"end":{"line":10,"column":47}},"type":"binary-expr","locations":[{"start":{"line":10,"column":11},"end":{"line":10,"column":33}},{"start":{"line":10,"column":37},"end":{"line":10,"column":47}}],"line":10}},"s":{"0":2,"1":2},"f":{},"b":{"0":[2,0],"1":[2,0]},"_coverageSchema":"43e27e138ebf9cfc5966b082cf9a028302ed4184","hash":"1677d0bce776d91397748f8a0a57de9b19c80b02"} 9 | ,"/Users/diegofernandes/www/masterclass/authtdd/src/database/migrations/20190219195005-create-users.js": {"path":"/Users/diegofernandes/www/masterclass/authtdd/src/database/migrations/20190219195005-create-users.js","statementMap":{"0":{"start":{"line":3,"column":0},"end":{"line":39,"column":2}},"1":{"start":{"line":5,"column":4},"end":{"line":33,"column":7}},"2":{"start":{"line":37,"column":4},"end":{"line":37,"column":45}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":4,"column":6},"end":{"line":4,"column":7}},"loc":{"start":{"line":4,"column":37},"end":{"line":34,"column":3}},"line":4},"1":{"name":"(anonymous_1)","decl":{"start":{"line":36,"column":8},"end":{"line":36,"column":9}},"loc":{"start":{"line":36,"column":39},"end":{"line":38,"column":3}},"line":36}},"branchMap":{},"s":{"0":0,"1":0,"2":0},"f":{"0":0,"1":0},"b":{}} 10 | } 11 | -------------------------------------------------------------------------------- /__tests__/coverage/lcov-report/base.css: -------------------------------------------------------------------------------- 1 | body, html { 2 | margin:0; padding: 0; 3 | height: 100%; 4 | } 5 | body { 6 | font-family: Helvetica Neue, Helvetica, Arial; 7 | font-size: 14px; 8 | color:#333; 9 | } 10 | .small { font-size: 12px; } 11 | *, *:after, *:before { 12 | -webkit-box-sizing:border-box; 13 | -moz-box-sizing:border-box; 14 | box-sizing:border-box; 15 | } 16 | h1 { font-size: 20px; margin: 0;} 17 | h2 { font-size: 14px; } 18 | pre { 19 | font: 12px/1.4 Consolas, "Liberation Mono", Menlo, Courier, monospace; 20 | margin: 0; 21 | padding: 0; 22 | -moz-tab-size: 2; 23 | -o-tab-size: 2; 24 | tab-size: 2; 25 | } 26 | a { color:#0074D9; text-decoration:none; } 27 | a:hover { text-decoration:underline; } 28 | .strong { font-weight: bold; } 29 | .space-top1 { padding: 10px 0 0 0; } 30 | .pad2y { padding: 20px 0; } 31 | .pad1y { padding: 10px 0; } 32 | .pad2x { padding: 0 20px; } 33 | .pad2 { padding: 20px; } 34 | .pad1 { padding: 10px; } 35 | .space-left2 { padding-left:55px; } 36 | .space-right2 { padding-right:20px; } 37 | .center { text-align:center; } 38 | .clearfix { display:block; } 39 | .clearfix:after { 40 | content:''; 41 | display:block; 42 | height:0; 43 | clear:both; 44 | visibility:hidden; 45 | } 46 | .fl { float: left; } 47 | @media only screen and (max-width:640px) { 48 | .col3 { width:100%; max-width:100%; } 49 | .hide-mobile { display:none!important; } 50 | } 51 | 52 | .quiet { 53 | color: #7f7f7f; 54 | color: rgba(0,0,0,0.5); 55 | } 56 | .quiet a { opacity: 0.7; } 57 | 58 | .fraction { 59 | font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; 60 | font-size: 10px; 61 | color: #555; 62 | background: #E8E8E8; 63 | padding: 4px 5px; 64 | border-radius: 3px; 65 | vertical-align: middle; 66 | } 67 | 68 | div.path a:link, div.path a:visited { color: #333; } 69 | table.coverage { 70 | border-collapse: collapse; 71 | margin: 10px 0 0 0; 72 | padding: 0; 73 | } 74 | 75 | table.coverage td { 76 | margin: 0; 77 | padding: 0; 78 | vertical-align: top; 79 | } 80 | table.coverage td.line-count { 81 | text-align: right; 82 | padding: 0 5px 0 20px; 83 | } 84 | table.coverage td.line-coverage { 85 | text-align: right; 86 | padding-right: 10px; 87 | min-width:20px; 88 | } 89 | 90 | table.coverage td span.cline-any { 91 | display: inline-block; 92 | padding: 0 5px; 93 | width: 100%; 94 | } 95 | .missing-if-branch { 96 | display: inline-block; 97 | margin-right: 5px; 98 | border-radius: 3px; 99 | position: relative; 100 | padding: 0 4px; 101 | background: #333; 102 | color: yellow; 103 | } 104 | 105 | .skip-if-branch { 106 | display: none; 107 | margin-right: 10px; 108 | position: relative; 109 | padding: 0 4px; 110 | background: #ccc; 111 | color: white; 112 | } 113 | .missing-if-branch .typ, .skip-if-branch .typ { 114 | color: inherit !important; 115 | } 116 | .coverage-summary { 117 | border-collapse: collapse; 118 | width: 100%; 119 | } 120 | .coverage-summary tr { border-bottom: 1px solid #bbb; } 121 | .keyline-all { border: 1px solid #ddd; } 122 | .coverage-summary td, .coverage-summary th { padding: 10px; } 123 | .coverage-summary tbody { border: 1px solid #bbb; } 124 | .coverage-summary td { border-right: 1px solid #bbb; } 125 | .coverage-summary td:last-child { border-right: none; } 126 | .coverage-summary th { 127 | text-align: left; 128 | font-weight: normal; 129 | white-space: nowrap; 130 | } 131 | .coverage-summary th.file { border-right: none !important; } 132 | .coverage-summary th.pct { } 133 | .coverage-summary th.pic, 134 | .coverage-summary th.abs, 135 | .coverage-summary td.pct, 136 | .coverage-summary td.abs { text-align: right; } 137 | .coverage-summary td.file { white-space: nowrap; } 138 | .coverage-summary td.pic { min-width: 120px !important; } 139 | .coverage-summary tfoot td { } 140 | 141 | .coverage-summary .sorter { 142 | height: 10px; 143 | width: 7px; 144 | display: inline-block; 145 | margin-left: 0.5em; 146 | background: url(sort-arrow-sprite.png) no-repeat scroll 0 0 transparent; 147 | } 148 | .coverage-summary .sorted .sorter { 149 | background-position: 0 -20px; 150 | } 151 | .coverage-summary .sorted-desc .sorter { 152 | background-position: 0 -10px; 153 | } 154 | .status-line { height: 10px; } 155 | /* yellow */ 156 | .cbranch-no { background: yellow !important; color: #111; } 157 | /* dark red */ 158 | .red.solid, .status-line.low, .low .cover-fill { background:#C21F39 } 159 | .low .chart { border:1px solid #C21F39 } 160 | .highlighted, 161 | .highlighted .cstat-no, .highlighted .fstat-no, .highlighted .cbranch-no{ 162 | background: #C21F39 !important; 163 | } 164 | /* medium red */ 165 | .cstat-no, .fstat-no, .cbranch-no, .cbranch-no { background:#F6C6CE } 166 | /* light red */ 167 | .low, .cline-no { background:#FCE1E5 } 168 | /* light green */ 169 | .high, .cline-yes { background:rgb(230,245,208) } 170 | /* medium green */ 171 | .cstat-yes { background:rgb(161,215,106) } 172 | /* dark green */ 173 | .status-line.high, .high .cover-fill { background:rgb(77,146,33) } 174 | .high .chart { border:1px solid rgb(77,146,33) } 175 | 176 | .medium .chart { border:1px solid #666; } 177 | .medium .cover-fill { background: #666; } 178 | 179 | .cstat-skip { background: #ddd; color: #111; } 180 | .fstat-skip { background: #ddd; color: #111 !important; } 181 | .cbranch-skip { background: #ddd !important; color: #111; } 182 | 183 | span.cline-neutral { background: #eaeaea; } 184 | .medium { background: #eaeaea; } 185 | 186 | .coverage-summary td.empty { 187 | opacity: .5; 188 | padding-top: 4px; 189 | padding-bottom: 4px; 190 | line-height: 1; 191 | color: #888; 192 | } 193 | 194 | .cover-fill, .cover-empty { 195 | display:inline-block; 196 | height: 12px; 197 | } 198 | .chart { 199 | line-height: 0; 200 | } 201 | .cover-empty { 202 | background: white; 203 | } 204 | .cover-full { 205 | border-right: none !important; 206 | } 207 | pre.prettyprint { 208 | border: none !important; 209 | padding: 0 !important; 210 | margin: 0 !important; 211 | } 212 | .com { color: #999 !important; } 213 | .ignore-none { color: #999; font-weight: normal; } 214 | 215 | .wrapper { 216 | min-height: 100%; 217 | height: auto !important; 218 | height: 100%; 219 | margin: 0 auto -48px; 220 | } 221 | .footer, .push { 222 | height: 48px; 223 | } 224 | -------------------------------------------------------------------------------- /__tests__/coverage/lcov-report/block-navigation.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | var jumpToCode = (function init() { 3 | // Classes of code we would like to highlight in the file view 4 | var missingCoverageClasses = ['.cbranch-no', '.cstat-no', '.fstat-no']; 5 | 6 | // Elements to highlight in the file listing view 7 | var fileListingElements = ['td.pct.low']; 8 | 9 | // We don't want to select elements that are direct descendants of another match 10 | var notSelector = ':not(' + missingCoverageClasses.join('):not(') + ') > '; // becomes `:not(a):not(b) > ` 11 | 12 | // Selecter that finds elements on the page to which we can jump 13 | var selector = 14 | fileListingElements.join(', ') + 15 | ', ' + 16 | notSelector + 17 | missingCoverageClasses.join(', ' + notSelector); // becomes `:not(a):not(b) > a, :not(a):not(b) > b` 18 | 19 | // The NodeList of matching elements 20 | var missingCoverageElements = document.querySelectorAll(selector); 21 | 22 | var currentIndex; 23 | 24 | function toggleClass(index) { 25 | missingCoverageElements 26 | .item(currentIndex) 27 | .classList.remove('highlighted'); 28 | missingCoverageElements.item(index).classList.add('highlighted'); 29 | } 30 | 31 | function makeCurrent(index) { 32 | toggleClass(index); 33 | currentIndex = index; 34 | missingCoverageElements.item(index).scrollIntoView({ 35 | behavior: 'smooth', 36 | block: 'center', 37 | inline: 'center' 38 | }); 39 | } 40 | 41 | function goToPrevious() { 42 | var nextIndex = 0; 43 | if (typeof currentIndex !== 'number' || currentIndex === 0) { 44 | nextIndex = missingCoverageElements.length - 1; 45 | } else if (missingCoverageElements.length > 1) { 46 | nextIndex = currentIndex - 1; 47 | } 48 | 49 | makeCurrent(nextIndex); 50 | } 51 | 52 | function goToNext() { 53 | var nextIndex = 0; 54 | 55 | if ( 56 | typeof currentIndex === 'number' && 57 | currentIndex < missingCoverageElements.length - 1 58 | ) { 59 | nextIndex = currentIndex + 1; 60 | } 61 | 62 | makeCurrent(nextIndex); 63 | } 64 | 65 | return function jump(event) { 66 | switch (event.which) { 67 | case 78: // n 68 | case 74: // j 69 | goToNext(); 70 | break; 71 | case 66: // b 72 | case 75: // k 73 | case 80: // p 74 | goToPrevious(); 75 | break; 76 | } 77 | }; 78 | })(); 79 | window.addEventListener('keydown', jumpToCode); 80 | -------------------------------------------------------------------------------- /__tests__/coverage/lcov-report/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Code coverage report for All files 5 | 6 | 7 | 8 | 9 | 14 | 15 | 16 |
17 |
18 |

19 | All files 20 |

21 |
22 |
23 | 90.28% 24 | Statements 25 | 65/72 26 |
27 |
28 | 61.9% 29 | Branches 30 | 13/21 31 |
32 |
33 | 86.67% 34 | Functions 35 | 13/15 36 |
37 |
38 | 90.28% 39 | Lines 40 | 65/72 41 |
42 |
43 |

44 | Press n or j to go to the next uncovered block, b, p or k for the previous block. 45 |

46 |
47 |
48 |
49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 |
FileStatementsBranchesFunctionsLines
src
88.89%16/1825%1/4100%4/488.89%16/18
src/app/controllers
88.89%8/975%3/4100%1/188.89%8/9
src/app/middleware
100%12/12100%2/2100%1/1100%12/12
src/app/models
96.43%27/2871.43%5/7100%7/796.43%27/28
src/config
100%2/250%2/4100%0/0100%2/2
src/database/migrations
0%0/3100%0/00%0/20%0/3
144 |
145 |
146 | 150 | 151 | 152 | 159 | 160 | 161 | 162 | 163 | -------------------------------------------------------------------------------- /__tests__/coverage/lcov-report/prettify.css: -------------------------------------------------------------------------------- 1 | .pln{color:#000}@media screen{.str{color:#080}.kwd{color:#008}.com{color:#800}.typ{color:#606}.lit{color:#066}.pun,.opn,.clo{color:#660}.tag{color:#008}.atn{color:#606}.atv{color:#080}.dec,.var{color:#606}.fun{color:red}}@media print,projection{.str{color:#060}.kwd{color:#006;font-weight:bold}.com{color:#600;font-style:italic}.typ{color:#404;font-weight:bold}.lit{color:#044}.pun,.opn,.clo{color:#440}.tag{color:#006;font-weight:bold}.atn{color:#404}.atv{color:#060}}pre.prettyprint{padding:2px;border:1px solid #888}ol.linenums{margin-top:0;margin-bottom:0}li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8{list-style-type:none}li.L1,li.L3,li.L5,li.L7,li.L9{background:#eee} 2 | -------------------------------------------------------------------------------- /__tests__/coverage/lcov-report/prettify.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | window.PR_SHOULD_USE_CONTINUATION=true;(function(){var h=["break,continue,do,else,for,if,return,while"];var u=[h,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"];var p=[u,"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"];var l=[p,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"];var x=[p,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"];var R=[x,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var"];var r="all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,true,try,unless,until,when,while,yes";var w=[p,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"];var s="caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END";var I=[h,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"];var f=[h,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"];var H=[h,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"];var A=[l,R,w,s+I,f,H];var e=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/;var C="str";var z="kwd";var j="com";var O="typ";var G="lit";var L="pun";var F="pln";var m="tag";var E="dec";var J="src";var P="atn";var n="atv";var N="nocode";var M="(?:^^\\.?|[+-]|\\!|\\!=|\\!==|\\#|\\%|\\%=|&|&&|&&=|&=|\\(|\\*|\\*=|\\+=|\\,|\\-=|\\->|\\/|\\/=|:|::|\\;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|\\?|\\@|\\[|\\^|\\^=|\\^\\^|\\^\\^=|\\{|\\||\\|=|\\|\\||\\|\\|=|\\~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\\s*";function k(Z){var ad=0;var S=false;var ac=false;for(var V=0,U=Z.length;V122)){if(!(al<65||ag>90)){af.push([Math.max(65,ag)|32,Math.min(al,90)|32])}if(!(al<97||ag>122)){af.push([Math.max(97,ag)&~32,Math.min(al,122)&~32])}}}}af.sort(function(av,au){return(av[0]-au[0])||(au[1]-av[1])});var ai=[];var ap=[NaN,NaN];for(var ar=0;arat[0]){if(at[1]+1>at[0]){an.push("-")}an.push(T(at[1]))}}an.push("]");return an.join("")}function W(al){var aj=al.source.match(new RegExp("(?:\\[(?:[^\\x5C\\x5D]|\\\\[\\s\\S])*\\]|\\\\u[A-Fa-f0-9]{4}|\\\\x[A-Fa-f0-9]{2}|\\\\[0-9]+|\\\\[^ux0-9]|\\(\\?[:!=]|[\\(\\)\\^]|[^\\x5B\\x5C\\(\\)\\^]+)","g"));var ah=aj.length;var an=[];for(var ak=0,am=0;ak=2&&ai==="["){aj[ak]=X(ag)}else{if(ai!=="\\"){aj[ak]=ag.replace(/[a-zA-Z]/g,function(ao){var ap=ao.charCodeAt(0);return"["+String.fromCharCode(ap&~32,ap|32)+"]"})}}}}return aj.join("")}var aa=[];for(var V=0,U=Z.length;V=0;){S[ac.charAt(ae)]=Y}}var af=Y[1];var aa=""+af;if(!ag.hasOwnProperty(aa)){ah.push(af);ag[aa]=null}}ah.push(/[\0-\uffff]/);V=k(ah)})();var X=T.length;var W=function(ah){var Z=ah.sourceCode,Y=ah.basePos;var ad=[Y,F];var af=0;var an=Z.match(V)||[];var aj={};for(var ae=0,aq=an.length;ae=5&&"lang-"===ap.substring(0,5);if(am&&!(ai&&typeof ai[1]==="string")){am=false;ap=J}if(!am){aj[ag]=ap}}var ab=af;af+=ag.length;if(!am){ad.push(Y+ab,ap)}else{var al=ai[1];var ak=ag.indexOf(al);var ac=ak+al.length;if(ai[2]){ac=ag.length-ai[2].length;ak=ac-al.length}var ar=ap.substring(5);B(Y+ab,ag.substring(0,ak),W,ad);B(Y+ab+ak,al,q(ar,al),ad);B(Y+ab+ac,ag.substring(ac),W,ad)}}ah.decorations=ad};return W}function i(T){var W=[],S=[];if(T.tripleQuotedStrings){W.push([C,/^(?:\'\'\'(?:[^\'\\]|\\[\s\S]|\'{1,2}(?=[^\']))*(?:\'\'\'|$)|\"\"\"(?:[^\"\\]|\\[\s\S]|\"{1,2}(?=[^\"]))*(?:\"\"\"|$)|\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$))/,null,"'\""])}else{if(T.multiLineStrings){W.push([C,/^(?:\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$)|\`(?:[^\\\`]|\\[\s\S])*(?:\`|$))/,null,"'\"`"])}else{W.push([C,/^(?:\'(?:[^\\\'\r\n]|\\.)*(?:\'|$)|\"(?:[^\\\"\r\n]|\\.)*(?:\"|$))/,null,"\"'"])}}if(T.verbatimStrings){S.push([C,/^@\"(?:[^\"]|\"\")*(?:\"|$)/,null])}var Y=T.hashComments;if(Y){if(T.cStyleComments){if(Y>1){W.push([j,/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,null,"#"])}else{W.push([j,/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\r\n]*)/,null,"#"])}S.push([C,/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,null])}else{W.push([j,/^#[^\r\n]*/,null,"#"])}}if(T.cStyleComments){S.push([j,/^\/\/[^\r\n]*/,null]);S.push([j,/^\/\*[\s\S]*?(?:\*\/|$)/,null])}if(T.regexLiterals){var X=("/(?=[^/*])(?:[^/\\x5B\\x5C]|\\x5C[\\s\\S]|\\x5B(?:[^\\x5C\\x5D]|\\x5C[\\s\\S])*(?:\\x5D|$))+/");S.push(["lang-regex",new RegExp("^"+M+"("+X+")")])}var V=T.types;if(V){S.push([O,V])}var U=(""+T.keywords).replace(/^ | $/g,"");if(U.length){S.push([z,new RegExp("^(?:"+U.replace(/[\s,]+/g,"|")+")\\b"),null])}W.push([F,/^\s+/,null," \r\n\t\xA0"]);S.push([G,/^@[a-z_$][a-z_$@0-9]*/i,null],[O,/^(?:[@_]?[A-Z]+[a-z][A-Za-z_$@0-9]*|\w+_t\b)/,null],[F,/^[a-z_$][a-z_$@0-9]*/i,null],[G,new RegExp("^(?:0x[a-f0-9]+|(?:\\d(?:_\\d+)*\\d*(?:\\.\\d*)?|\\.\\d\\+)(?:e[+\\-]?\\d+)?)[a-z]*","i"),null,"0123456789"],[F,/^\\[\s\S]?/,null],[L,/^.[^\s\w\.$@\'\"\`\/\#\\]*/,null]);return g(W,S)}var K=i({keywords:A,hashComments:true,cStyleComments:true,multiLineStrings:true,regexLiterals:true});function Q(V,ag){var U=/(?:^|\s)nocode(?:\s|$)/;var ab=/\r\n?|\n/;var ac=V.ownerDocument;var S;if(V.currentStyle){S=V.currentStyle.whiteSpace}else{if(window.getComputedStyle){S=ac.defaultView.getComputedStyle(V,null).getPropertyValue("white-space")}}var Z=S&&"pre"===S.substring(0,3);var af=ac.createElement("LI");while(V.firstChild){af.appendChild(V.firstChild)}var W=[af];function ae(al){switch(al.nodeType){case 1:if(U.test(al.className)){break}if("BR"===al.nodeName){ad(al);if(al.parentNode){al.parentNode.removeChild(al)}}else{for(var an=al.firstChild;an;an=an.nextSibling){ae(an)}}break;case 3:case 4:if(Z){var am=al.nodeValue;var aj=am.match(ab);if(aj){var ai=am.substring(0,aj.index);al.nodeValue=ai;var ah=am.substring(aj.index+aj[0].length);if(ah){var ak=al.parentNode;ak.insertBefore(ac.createTextNode(ah),al.nextSibling)}ad(al);if(!ai){al.parentNode.removeChild(al)}}}break}}function ad(ak){while(!ak.nextSibling){ak=ak.parentNode;if(!ak){return}}function ai(al,ar){var aq=ar?al.cloneNode(false):al;var ao=al.parentNode;if(ao){var ap=ai(ao,1);var an=al.nextSibling;ap.appendChild(aq);for(var am=an;am;am=an){an=am.nextSibling;ap.appendChild(am)}}return aq}var ah=ai(ak.nextSibling,0);for(var aj;(aj=ah.parentNode)&&aj.nodeType===1;){ah=aj}W.push(ah)}for(var Y=0;Y=S){ah+=2}if(V>=ap){Z+=2}}}var t={};function c(U,V){for(var S=V.length;--S>=0;){var T=V[S];if(!t.hasOwnProperty(T)){t[T]=U}else{if(window.console){console.warn("cannot override language handler %s",T)}}}}function q(T,S){if(!(T&&t.hasOwnProperty(T))){T=/^\s*]*(?:>|$)/],[j,/^<\!--[\s\S]*?(?:-\->|$)/],["lang-",/^<\?([\s\S]+?)(?:\?>|$)/],["lang-",/^<%([\s\S]+?)(?:%>|$)/],[L,/^(?:<[%?]|[%?]>)/],["lang-",/^]*>([\s\S]+?)<\/xmp\b[^>]*>/i],["lang-js",/^]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\s\S]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]),["default-markup","htm","html","mxml","xhtml","xml","xsl"]);c(g([[F,/^[\s]+/,null," \t\r\n"],[n,/^(?:\"[^\"]*\"?|\'[^\']*\'?)/,null,"\"'"]],[[m,/^^<\/?[a-z](?:[\w.:-]*\w)?|\/?>$/i],[P,/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^>\'\"\s]*(?:[^>\'\"\s\/]|\/(?=\s)))/],[L,/^[=<>\/]+/],["lang-js",/^on\w+\s*=\s*\"([^\"]+)\"/i],["lang-js",/^on\w+\s*=\s*\'([^\']+)\'/i],["lang-js",/^on\w+\s*=\s*([^\"\'>\s]+)/i],["lang-css",/^style\s*=\s*\"([^\"]+)\"/i],["lang-css",/^style\s*=\s*\'([^\']+)\'/i],["lang-css",/^style\s*=\s*([^\"\'>\s]+)/i]]),["in.tag"]);c(g([],[[n,/^[\s\S]+/]]),["uq.val"]);c(i({keywords:l,hashComments:true,cStyleComments:true,types:e}),["c","cc","cpp","cxx","cyc","m"]);c(i({keywords:"null,true,false"}),["json"]);c(i({keywords:R,hashComments:true,cStyleComments:true,verbatimStrings:true,types:e}),["cs"]);c(i({keywords:x,cStyleComments:true}),["java"]);c(i({keywords:H,hashComments:true,multiLineStrings:true}),["bsh","csh","sh"]);c(i({keywords:I,hashComments:true,multiLineStrings:true,tripleQuotedStrings:true}),["cv","py"]);c(i({keywords:s,hashComments:true,multiLineStrings:true,regexLiterals:true}),["perl","pl","pm"]);c(i({keywords:f,hashComments:true,multiLineStrings:true,regexLiterals:true}),["rb"]);c(i({keywords:w,cStyleComments:true,regexLiterals:true}),["js"]);c(i({keywords:r,hashComments:3,cStyleComments:true,multilineStrings:true,tripleQuotedStrings:true,regexLiterals:true}),["coffee"]);c(g([],[[C,/^[\s\S]+/]]),["regex"]);function d(V){var U=V.langExtension;try{var S=a(V.sourceNode);var T=S.sourceCode;V.sourceCode=T;V.spans=S.spans;V.basePos=0;q(U,T)(V);D(V)}catch(W){if("console" in window){console.log(W&&W.stack?W.stack:W)}}}function y(W,V,U){var S=document.createElement("PRE");S.innerHTML=W;if(U){Q(S,U)}var T={langExtension:V,numberLines:U,sourceNode:S};d(T);return S.innerHTML}function b(ad){function Y(af){return document.getElementsByTagName(af)}var ac=[Y("pre"),Y("code"),Y("xmp")];var T=[];for(var aa=0;aa=0){var ah=ai.match(ab);var am;if(!ah&&(am=o(aj))&&"CODE"===am.tagName){ah=am.className.match(ab)}if(ah){ah=ah[1]}var al=false;for(var ak=aj.parentNode;ak;ak=ak.parentNode){if((ak.tagName==="pre"||ak.tagName==="code"||ak.tagName==="xmp")&&ak.className&&ak.className.indexOf("prettyprint")>=0){al=true;break}}if(!al){var af=aj.className.match(/\blinenums\b(?::(\d+))?/);af=af?af[1]&&af[1].length?+af[1]:true:false;if(af){Q(aj,af)}S={langExtension:ah,sourceNode:aj,numberLines:af};d(S)}}}if(X]*(?:>|$)/],[PR.PR_COMMENT,/^<\!--[\s\S]*?(?:-\->|$)/],[PR.PR_PUNCTUATION,/^(?:<[%?]|[%?]>)/],["lang-",/^<\?([\s\S]+?)(?:\?>|$)/],["lang-",/^<%([\s\S]+?)(?:%>|$)/],["lang-",/^]*>([\s\S]+?)<\/xmp\b[^>]*>/i],["lang-handlebars",/^]*type\s*=\s*['"]?text\/x-handlebars-template['"]?\b[^>]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-js",/^]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\s\S]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i],[PR.PR_DECLARATION,/^{{[#^>/]?\s*[\w.][^}]*}}/],[PR.PR_DECLARATION,/^{{&?\s*[\w.][^}]*}}/],[PR.PR_DECLARATION,/^{{{>?\s*[\w.][^}]*}}}/],[PR.PR_COMMENT,/^{{![^}]*}}/]]),["handlebars","hbs"]);PR.registerLangHandler(PR.createSimpleLexer([[PR.PR_PLAIN,/^[ \t\r\n\f]+/,null," \t\r\n\f"]],[[PR.PR_STRING,/^\"(?:[^\n\r\f\\\"]|\\(?:\r\n?|\n|\f)|\\[\s\S])*\"/,null],[PR.PR_STRING,/^\'(?:[^\n\r\f\\\']|\\(?:\r\n?|\n|\f)|\\[\s\S])*\'/,null],["lang-css-str",/^url\(([^\)\"\']*)\)/i],[PR.PR_KEYWORD,/^(?:url|rgb|\!important|@import|@page|@media|@charset|inherit)(?=[^\-\w]|$)/i,null],["lang-css-kw",/^(-?(?:[_a-z]|(?:\\[0-9a-f]+ ?))(?:[_a-z0-9\-]|\\(?:\\[0-9a-f]+ ?))*)\s*:/i],[PR.PR_COMMENT,/^\/\*[^*]*\*+(?:[^\/*][^*]*\*+)*\//],[PR.PR_COMMENT,/^(?:)/],[PR.PR_LITERAL,/^(?:\d+|\d*\.\d+)(?:%|[a-z]+)?/i],[PR.PR_LITERAL,/^#(?:[0-9a-f]{3}){1,2}/i],[PR.PR_PLAIN,/^-?(?:[_a-z]|(?:\\[\da-f]+ ?))(?:[_a-z\d\-]|\\(?:\\[\da-f]+ ?))*/i],[PR.PR_PUNCTUATION,/^[^\s\w\'\"]+/]]),["css"]);PR.registerLangHandler(PR.createSimpleLexer([],[[PR.PR_KEYWORD,/^-?(?:[_a-z]|(?:\\[\da-f]+ ?))(?:[_a-z\d\-]|\\(?:\\[\da-f]+ ?))*/i]]),["css-kw"]);PR.registerLangHandler(PR.createSimpleLexer([],[[PR.PR_STRING,/^[^\)\"\']+/]]),["css-str"]); 3 | -------------------------------------------------------------------------------- /__tests__/coverage/lcov-report/sort-arrow-sprite.png: -------------------------------------------------------------------------------- 1 | �PNG 2 |  3 | IHDR�F�3tEXtSoftwareAdobe ImageReadyq�e<sIDATxڼRA� ������D���e[ � 4 | �HfFX� g�f���3�Dd F�w�-˘�� 5 | � n�g�M+OVں�=�""/�ĥ'Y���?H�hV� �@��p���Y�8Bwc,�IEND�B`� -------------------------------------------------------------------------------- /__tests__/coverage/lcov-report/sorter.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | var addSorting = (function() { 3 | 'use strict'; 4 | var cols, 5 | currentSort = { 6 | index: 0, 7 | desc: false 8 | }; 9 | 10 | // returns the summary table element 11 | function getTable() { 12 | return document.querySelector('.coverage-summary'); 13 | } 14 | // returns the thead element of the summary table 15 | function getTableHeader() { 16 | return getTable().querySelector('thead tr'); 17 | } 18 | // returns the tbody element of the summary table 19 | function getTableBody() { 20 | return getTable().querySelector('tbody'); 21 | } 22 | // returns the th element for nth column 23 | function getNthColumn(n) { 24 | return getTableHeader().querySelectorAll('th')[n]; 25 | } 26 | 27 | // loads all columns 28 | function loadColumns() { 29 | var colNodes = getTableHeader().querySelectorAll('th'), 30 | colNode, 31 | cols = [], 32 | col, 33 | i; 34 | 35 | for (i = 0; i < colNodes.length; i += 1) { 36 | colNode = colNodes[i]; 37 | col = { 38 | key: colNode.getAttribute('data-col'), 39 | sortable: !colNode.getAttribute('data-nosort'), 40 | type: colNode.getAttribute('data-type') || 'string' 41 | }; 42 | cols.push(col); 43 | if (col.sortable) { 44 | col.defaultDescSort = col.type === 'number'; 45 | colNode.innerHTML = 46 | colNode.innerHTML + ''; 47 | } 48 | } 49 | return cols; 50 | } 51 | // attaches a data attribute to every tr element with an object 52 | // of data values keyed by column name 53 | function loadRowData(tableRow) { 54 | var tableCols = tableRow.querySelectorAll('td'), 55 | colNode, 56 | col, 57 | data = {}, 58 | i, 59 | val; 60 | for (i = 0; i < tableCols.length; i += 1) { 61 | colNode = tableCols[i]; 62 | col = cols[i]; 63 | val = colNode.getAttribute('data-value'); 64 | if (col.type === 'number') { 65 | val = Number(val); 66 | } 67 | data[col.key] = val; 68 | } 69 | return data; 70 | } 71 | // loads all row data 72 | function loadData() { 73 | var rows = getTableBody().querySelectorAll('tr'), 74 | i; 75 | 76 | for (i = 0; i < rows.length; i += 1) { 77 | rows[i].data = loadRowData(rows[i]); 78 | } 79 | } 80 | // sorts the table using the data for the ith column 81 | function sortByIndex(index, desc) { 82 | var key = cols[index].key, 83 | sorter = function(a, b) { 84 | a = a.data[key]; 85 | b = b.data[key]; 86 | return a < b ? -1 : a > b ? 1 : 0; 87 | }, 88 | finalSorter = sorter, 89 | tableBody = document.querySelector('.coverage-summary tbody'), 90 | rowNodes = tableBody.querySelectorAll('tr'), 91 | rows = [], 92 | i; 93 | 94 | if (desc) { 95 | finalSorter = function(a, b) { 96 | return -1 * sorter(a, b); 97 | }; 98 | } 99 | 100 | for (i = 0; i < rowNodes.length; i += 1) { 101 | rows.push(rowNodes[i]); 102 | tableBody.removeChild(rowNodes[i]); 103 | } 104 | 105 | rows.sort(finalSorter); 106 | 107 | for (i = 0; i < rows.length; i += 1) { 108 | tableBody.appendChild(rows[i]); 109 | } 110 | } 111 | // removes sort indicators for current column being sorted 112 | function removeSortIndicators() { 113 | var col = getNthColumn(currentSort.index), 114 | cls = col.className; 115 | 116 | cls = cls.replace(/ sorted$/, '').replace(/ sorted-desc$/, ''); 117 | col.className = cls; 118 | } 119 | // adds sort indicators for current column being sorted 120 | function addSortIndicators() { 121 | getNthColumn(currentSort.index).className += currentSort.desc 122 | ? ' sorted-desc' 123 | : ' sorted'; 124 | } 125 | // adds event listeners for all sorter widgets 126 | function enableUI() { 127 | var i, 128 | el, 129 | ithSorter = function ithSorter(i) { 130 | var col = cols[i]; 131 | 132 | return function() { 133 | var desc = col.defaultDescSort; 134 | 135 | if (currentSort.index === i) { 136 | desc = !currentSort.desc; 137 | } 138 | sortByIndex(i, desc); 139 | removeSortIndicators(); 140 | currentSort.index = i; 141 | currentSort.desc = desc; 142 | addSortIndicators(); 143 | }; 144 | }; 145 | for (i = 0; i < cols.length; i += 1) { 146 | if (cols[i].sortable) { 147 | // add the click event handler on the th so users 148 | // dont have to click on those tiny arrows 149 | el = getNthColumn(i).querySelector('.sorter').parentElement; 150 | if (el.addEventListener) { 151 | el.addEventListener('click', ithSorter(i)); 152 | } else { 153 | el.attachEvent('onclick', ithSorter(i)); 154 | } 155 | } 156 | } 157 | } 158 | // adds sorting functionality to the UI 159 | return function() { 160 | if (!getTable()) { 161 | return; 162 | } 163 | cols = loadColumns(); 164 | loadData(cols); 165 | addSortIndicators(); 166 | enableUI(); 167 | }; 168 | })(); 169 | 170 | window.addEventListener('load', addSorting); 171 | -------------------------------------------------------------------------------- /__tests__/coverage/lcov-report/src/app.js.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Code coverage report for src/app.js 5 | 6 | 7 | 8 | 9 | 14 | 15 | 16 |
17 |
18 |

19 | All files / src app.js 20 |

21 |
22 |
23 | 100% 24 | Statements 25 | 8/8 26 |
27 |
28 | 50% 29 | Branches 30 | 1/2 31 |
32 |
33 | 100% 34 | Functions 35 | 3/3 36 |
37 |
38 | 100% 39 | Lines 40 | 8/8 41 |
42 |
43 |

44 | Press n or j to go to the next uncovered block, b, p or k for the previous block. 45 |

46 |
47 |
48 |

 49 | 
122 | 
1 50 | 2 51 | 3 52 | 4 53 | 5 54 | 6 55 | 7 56 | 8 57 | 9 58 | 10 59 | 11 60 | 12 61 | 13 62 | 14 63 | 15 64 | 16 65 | 17 66 | 18 67 | 19 68 | 20 69 | 21 70 | 22 71 | 23 72 | 24 73 | 251x 74 |   75 |   76 |   77 | 1x 78 |   79 |   80 |   81 | 1x 82 |   83 | 1x 84 | 1x 85 |   86 |   87 |   88 | 1x 89 |   90 |   91 |   92 | 1x 93 |   94 |   95 |   96 | 1x 97 |  
require("dotenv").config({
 98 |   path: process.env.NODE_ENV === "test" ? ".env.test" : ".env"
 99 | });
100 |  
101 | const express = require("express");
102 |  
103 | class AppController {
104 |   constructor() {
105 |     this.express = express();
106 |  
107 |     this.middlewares();
108 |     this.routes();
109 |   }
110 |  
111 |   middlewares() {
112 |     this.express.use(express.json());
113 |   }
114 |  
115 |   routes() {
116 |     this.express.use(require("./routes"));
117 |   }
118 | }
119 |  
120 | module.exports = new AppController().express;
121 |  
123 |
124 |
125 | 129 | 130 | 131 | 138 | 139 | 140 | 141 | 142 | -------------------------------------------------------------------------------- /__tests__/coverage/lcov-report/src/app/controllers/SessionController.js.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Code coverage report for src/app/controllers/SessionController.js 5 | 6 | 7 | 8 | 9 | 14 | 15 | 16 |
17 |
18 |

19 | All files / src/app/controllers SessionController.js 20 |

21 |
22 |
23 | 88.89% 24 | Statements 25 | 8/9 26 |
27 |
28 | 75% 29 | Branches 30 | 3/4 31 |
32 |
33 | 100% 34 | Functions 35 | 1/1 36 |
37 |
38 | 88.89% 39 | Lines 40 | 8/9 41 |
42 |
43 |

44 | Press n or j to go to the next uncovered block, b, p or k for the previous block. 45 |

46 |
47 |
48 |

 49 | 
122 | 
1 50 | 2 51 | 3 52 | 4 53 | 5 54 | 6 55 | 7 56 | 8 57 | 9 58 | 10 59 | 11 60 | 12 61 | 13 62 | 14 63 | 15 64 | 16 65 | 17 66 | 18 67 | 19 68 | 20 69 | 21 70 | 22 71 | 23 72 | 24 73 | 251x 74 |   75 |   76 |   77 | 3x 78 |   79 | 3x 80 |   81 | 3x 82 |   83 |   84 |   85 | 3x 86 | 1x 87 |   88 |   89 | 2x 90 |   91 |   92 |   93 |   94 |   95 |   96 | 1x 97 |  
const { User } = require("../models");
 98 |  
 99 | class SessionController {
100 |   async store(req, res) {
101 |     const { email, password } = req.body;
102 |  
103 |     const user = await User.findOne({ where: { email } });
104 |  
105 |     Iif (!user) {
106 |       return res.status(401).json({ message: "User not found" });
107 |     }
108 |  
109 |     if (!(await user.checkPassword(password))) {
110 |       return res.status(401).json({ message: "Incorrect password" });
111 |     }
112 |  
113 |     return res.json({
114 |       user,
115 |       token: user.generateToken()
116 |     });
117 |   }
118 | }
119 |  
120 | module.exports = new SessionController();
121 |  
123 |
124 |
125 | 129 | 130 | 131 | 138 | 139 | 140 | 141 | 142 | -------------------------------------------------------------------------------- /__tests__/coverage/lcov-report/src/app/controllers/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Code coverage report for src/app/controllers 5 | 6 | 7 | 8 | 9 | 14 | 15 | 16 |
17 |
18 |

19 | All files src/app/controllers 20 |

21 |
22 |
23 | 88.89% 24 | Statements 25 | 8/9 26 |
27 |
28 | 75% 29 | Branches 30 | 3/4 31 |
32 |
33 | 100% 34 | Functions 35 | 1/1 36 |
37 |
38 | 88.89% 39 | Lines 40 | 8/9 41 |
42 |
43 |

44 | Press n or j to go to the next uncovered block, b, p or k for the previous block. 45 |

46 |
47 |
48 |
49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 |
FileStatementsBranchesFunctionsLines
SessionController.js
88.89%8/975%3/4100%1/188.89%8/9
79 |
80 |
81 | 85 | 86 | 87 | 94 | 95 | 96 | 97 | 98 | -------------------------------------------------------------------------------- /__tests__/coverage/lcov-report/src/app/middleware/auth.js.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Code coverage report for src/app/middleware/auth.js 5 | 6 | 7 | 8 | 9 | 14 | 15 | 16 |
17 |
18 |

19 | All files / src/app/middleware auth.js 20 |

21 |
22 |
23 | 100% 24 | Statements 25 | 12/12 26 |
27 |
28 | 100% 29 | Branches 30 | 2/2 31 |
32 |
33 | 100% 34 | Functions 35 | 1/1 36 |
37 |
38 | 100% 39 | Lines 40 | 12/12 41 |
42 |
43 |

44 | Press n or j to go to the next uncovered block, b, p or k for the previous block. 45 |

46 |
47 |
48 |

 49 | 
116 | 
1 50 | 2 51 | 3 52 | 4 53 | 5 54 | 6 55 | 7 56 | 8 57 | 9 58 | 10 59 | 11 60 | 12 61 | 13 62 | 14 63 | 15 64 | 16 65 | 17 66 | 18 67 | 19 68 | 20 69 | 21 70 | 22 71 | 231x 72 | 1x 73 |   74 | 1x 75 | 3x 76 |   77 | 3x 78 | 1x 79 |   80 |   81 | 2x 82 |   83 | 2x 84 | 2x 85 |   86 | 1x 87 |   88 | 1x 89 |   90 | 1x 91 |   92 |   93 |  
const jwt = require("jsonwebtoken");
 94 | const { promisify } = require("util");
 95 |  
 96 | module.exports = async (req, res, next) => {
 97 |   const authHeader = req.headers.authorization;
 98 |  
 99 |   if (!authHeader) {
100 |     return res.status(401).json({ message: "Token not provided" });
101 |   }
102 |  
103 |   const [, token] = authHeader.split(" ");
104 |  
105 |   try {
106 |     const decoded = await promisify(jwt.verify)(token, process.env.APP_SECRET);
107 |  
108 |     req.userId = decoded.id;
109 |  
110 |     return next();
111 |   } catch (err) {
112 |     return res.status(401).json({ message: "Token invalid" });
113 |   }
114 | };
115 |  
117 |
118 |
119 | 123 | 124 | 125 | 132 | 133 | 134 | 135 | 136 | -------------------------------------------------------------------------------- /__tests__/coverage/lcov-report/src/app/middleware/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Code coverage report for src/app/middleware 5 | 6 | 7 | 8 | 9 | 14 | 15 | 16 |
17 |
18 |

19 | All files src/app/middleware 20 |

21 |
22 |
23 | 100% 24 | Statements 25 | 12/12 26 |
27 |
28 | 100% 29 | Branches 30 | 2/2 31 |
32 |
33 | 100% 34 | Functions 35 | 1/1 36 |
37 |
38 | 100% 39 | Lines 40 | 12/12 41 |
42 |
43 |

44 | Press n or j to go to the next uncovered block, b, p or k for the previous block. 45 |

46 |
47 |
48 |
49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 |
FileStatementsBranchesFunctionsLines
auth.js
100%12/12100%2/2100%1/1100%12/12
79 |
80 |
81 | 85 | 86 | 87 | 94 | 95 | 96 | 97 | 98 | -------------------------------------------------------------------------------- /__tests__/coverage/lcov-report/src/app/models/User.js.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Code coverage report for src/app/models/User.js 5 | 6 | 7 | 8 | 9 | 14 | 15 | 16 |
17 |
18 |

19 | All files / src/app/models User.js 20 |

21 |
22 |
23 | 100% 24 | Statements 25 | 11/11 26 |
27 |
28 | 50% 29 | Branches 30 | 1/2 31 |
32 |
33 | 100% 34 | Functions 35 | 4/4 36 |
37 |
38 | 100% 39 | Lines 40 | 11/11 41 |
42 |
43 |

44 | Press n or j to go to the next uncovered block, b, p or k for the previous block. 45 |

46 |
47 |
48 |

 49 | 
149 | 
1 50 | 2 51 | 3 52 | 4 53 | 5 54 | 6 55 | 7 56 | 8 57 | 9 58 | 10 59 | 11 60 | 12 61 | 13 62 | 14 63 | 15 64 | 16 65 | 17 66 | 18 67 | 19 68 | 20 69 | 21 70 | 22 71 | 23 72 | 24 73 | 25 74 | 26 75 | 27 76 | 28 77 | 29 78 | 30 79 | 31 80 | 32 81 | 33 82 | 342x 83 | 2x 84 |   85 | 2x 86 | 2x 87 |   88 |   89 |   90 |   91 |   92 |   93 |   94 |   95 |   96 |   97 | 5x 98 | 5x 99 |   100 |   101 |   102 |   103 |   104 |   105 | 2x 106 | 3x 107 |   108 |   109 | 2x 110 | 3x 111 |   112 |   113 | 2x 114 |   115 |  
const bcrypt = require("bcryptjs");
116 | const jwt = require("jsonwebtoken");
117 |  
118 | module.exports = (sequelize, DataTypes) => {
119 |   const User = sequelize.define(
120 |     "User",
121 |     {
122 |       name: DataTypes.STRING,
123 |       email: DataTypes.STRING,
124 |       password: DataTypes.VIRTUAL,
125 |       password_hash: DataTypes.STRING
126 |     },
127 |     {
128 |       hooks: {
129 |         beforeSave: async user => {
130 |           Eif (user.password) {
131 |             user.password_hash = await bcrypt.hash(user.password, 8);
132 |           }
133 |         }
134 |       }
135 |     }
136 |   );
137 |  
138 |   User.prototype.checkPassword = function(password) {
139 |     return bcrypt.compare(password, this.password_hash);
140 |   };
141 |  
142 |   User.prototype.generateToken = function() {
143 |     return jwt.sign({ id: this.id }, process.env.APP_SECRET);
144 |   };
145 |  
146 |   return User;
147 | };
148 |  
150 |
151 |
152 | 156 | 157 | 158 | 165 | 166 | 167 | 168 | 169 | -------------------------------------------------------------------------------- /__tests__/coverage/lcov-report/src/app/models/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Code coverage report for src/app/models 5 | 6 | 7 | 8 | 9 | 14 | 15 | 16 |
17 |
18 |

19 | All files src/app/models 20 |

21 |
22 |
23 | 96.43% 24 | Statements 25 | 27/28 26 |
27 |
28 | 71.43% 29 | Branches 30 | 5/7 31 |
32 |
33 | 100% 34 | Functions 35 | 7/7 36 |
37 |
38 | 96.43% 39 | Lines 40 | 27/28 41 |
42 |
43 |

44 | Press n or j to go to the next uncovered block, b, p or k for the previous block. 45 |

46 |
47 |
48 |
49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 |
FileStatementsBranchesFunctionsLines
User.js
100%11/1150%1/2100%4/4100%11/11
index.js
94.12%16/1780%4/5100%3/394.12%16/17
92 |
93 |
94 | 98 | 99 | 100 | 107 | 108 | 109 | 110 | 111 | -------------------------------------------------------------------------------- /__tests__/coverage/lcov-report/src/app/models/index.js.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Code coverage report for src/app/models/index.js 5 | 6 | 7 | 8 | 9 | 14 | 15 | 16 |
17 |
18 |

19 | All files / src/app/models index.js 20 |

21 |
22 |
23 | 94.12% 24 | Statements 25 | 16/17 26 |
27 |
28 | 80% 29 | Branches 30 | 4/5 31 |
32 |
33 | 100% 34 | Functions 35 | 3/3 36 |
37 |
38 | 94.12% 39 | Lines 40 | 16/17 41 |
42 |
43 |

44 | Press n or j to go to the next uncovered block, b, p or k for the previous block. 45 |

46 |
47 |
48 |

 49 | 
161 | 
1 50 | 2 51 | 3 52 | 4 53 | 5 54 | 6 55 | 7 56 | 8 57 | 9 58 | 10 59 | 11 60 | 12 61 | 13 62 | 14 63 | 15 64 | 16 65 | 17 66 | 18 67 | 19 68 | 20 69 | 21 70 | 22 71 | 23 72 | 24 73 | 25 74 | 26 75 | 27 76 | 28 77 | 29 78 | 30 79 | 31 80 | 32 81 | 33 82 | 34 83 | 35 84 | 36 85 | 37 86 | 38  87 |   88 | 2x 89 | 2x 90 | 2x 91 | 2x 92 | 2x 93 | 2x 94 |   95 | 2x 96 |   97 |   98 |   99 |   100 |   101 |   102 | 2x 103 |   104 | 4x 105 |   106 |   107 |   108 |   109 | 2x 110 | 2x 111 |   112 |   113 | 2x 114 | 2x 115 |   116 |   117 |   118 |   119 | 2x 120 | 2x 121 |   122 | 2x 123 |  
"use strict";
124 |  
125 | const fs = require("fs");
126 | const path = require("path");
127 | const Sequelize = require("sequelize");
128 | const basename = path.basename(__filename);
129 | const config = require("../../config/database");
130 | const db = {};
131 |  
132 | const sequelize = new Sequelize(
133 |   config.database,
134 |   config.username,
135 |   config.password,
136 |   config
137 | );
138 |  
139 | fs.readdirSync(__dirname)
140 |   .filter(file => {
141 |     return (
142 |       file.indexOf(".") !== 0 && file !== basename && file.slice(-3) === ".js"
143 |     );
144 |   })
145 |   .forEach(file => {
146 |     const model = sequelize["import"](path.join(__dirname, file));
147 |     db[model.name] = model;
148 |   });
149 |  
150 | Object.keys(db).forEach(modelName => {
151 |   Iif (db[modelName].associate) {
152 |     db[modelName].associate(db);
153 |   }
154 | });
155 |  
156 | db.sequelize = sequelize;
157 | db.Sequelize = Sequelize;
158 |  
159 | module.exports = db;
160 |  
162 |
163 |
164 | 168 | 169 | 170 | 177 | 178 | 179 | 180 | 181 | -------------------------------------------------------------------------------- /__tests__/coverage/lcov-report/src/config/database.js.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Code coverage report for src/config/database.js 5 | 6 | 7 | 8 | 9 | 14 | 15 | 16 |
17 |
18 |

19 | All files / src/config database.js 20 |

21 |
22 |
23 | 100% 24 | Statements 25 | 2/2 26 |
27 |
28 | 50% 29 | Branches 30 | 2/4 31 |
32 |
33 | 100% 34 | Functions 35 | 0/0 36 |
37 |
38 | 100% 39 | Lines 40 | 2/2 41 |
42 |
43 |

44 | Press n or j to go to the next uncovered block, b, p or k for the previous block. 45 |

46 |
47 |
48 |

 49 | 
107 | 
1 50 | 2 51 | 3 52 | 4 53 | 5 54 | 6 55 | 7 56 | 8 57 | 9 58 | 10 59 | 11 60 | 12 61 | 13 62 | 14 63 | 15 64 | 16 65 | 17 66 | 18 67 | 19 68 | 202x 69 |   70 |   71 |   72 | 2x 73 |   74 |   75 |   76 |   77 |   78 |   79 |   80 |   81 |   82 |   83 |   84 |   85 |   86 |   87 |  
require("dotenv").config({
 88 |   path: process.env.NODE_ENV === "test" ? ".env.test" : ".env"
 89 | });
 90 |  
 91 | module.exports = {
 92 |   host: process.env.DB_HOST,
 93 |   username: process.env.DB_USER,
 94 |   password: process.env.DB_PASS,
 95 |   database: process.env.DB_NAME,
 96 |   dialect: process.env.DB_DIALECT || "postgres",
 97 |   storage: "./__tests__/database.sqlite",
 98 |   operatorsAliases: false,
 99 |   logging: false,
100 |   define: {
101 |     timestamps: true,
102 |     underscored: true,
103 |     underscoredAll: true
104 |   }
105 | };
106 |  
108 |
109 |
110 | 114 | 115 | 116 | 123 | 124 | 125 | 126 | 127 | -------------------------------------------------------------------------------- /__tests__/coverage/lcov-report/src/config/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Code coverage report for src/config 5 | 6 | 7 | 8 | 9 | 14 | 15 | 16 |
17 |
18 |

19 | All files src/config 20 |

21 |
22 |
23 | 100% 24 | Statements 25 | 2/2 26 |
27 |
28 | 50% 29 | Branches 30 | 2/4 31 |
32 |
33 | 100% 34 | Functions 35 | 0/0 36 |
37 |
38 | 100% 39 | Lines 40 | 2/2 41 |
42 |
43 |

44 | Press n or j to go to the next uncovered block, b, p or k for the previous block. 45 |

46 |
47 |
48 |
49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 |
FileStatementsBranchesFunctionsLines
database.js
100%2/250%2/4100%0/0100%2/2
79 |
80 |
81 | 85 | 86 | 87 | 94 | 95 | 96 | 97 | 98 | -------------------------------------------------------------------------------- /__tests__/coverage/lcov-report/src/database/migrations/20190219195005-create-users.js.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Code coverage report for src/database/migrations/20190219195005-create-users.js 5 | 6 | 7 | 8 | 9 | 14 | 15 | 16 |
17 |
18 |

19 | All files / src/database/migrations 20190219195005-create-users.js 20 |

21 |
22 |
23 | 0% 24 | Statements 25 | 0/3 26 |
27 |
28 | 100% 29 | Branches 30 | 0/0 31 |
32 |
33 | 0% 34 | Functions 35 | 0/2 36 |
37 |
38 | 0% 39 | Lines 40 | 0/3 41 |
42 |
43 |

44 | Press n or j to go to the next uncovered block, b, p or k for the previous block. 45 |

46 |
47 |
48 |

 49 | 
167 | 
1 50 | 2 51 | 3 52 | 4 53 | 5 54 | 6 55 | 7 56 | 8 57 | 9 58 | 10 59 | 11 60 | 12 61 | 13 62 | 14 63 | 15 64 | 16 65 | 17 66 | 18 67 | 19 68 | 20 69 | 21 70 | 22 71 | 23 72 | 24 73 | 25 74 | 26 75 | 27 76 | 28 77 | 29 78 | 30 79 | 31 80 | 32 81 | 33 82 | 34 83 | 35 84 | 36 85 | 37 86 | 38 87 | 39 88 | 40  89 |   90 |   91 |   92 |   93 |   94 |   95 |   96 |   97 |   98 |   99 |   100 |   101 |   102 |   103 |   104 |   105 |   106 |   107 |   108 |   109 |   110 |   111 |   112 |   113 |   114 |   115 |   116 |   117 |   118 |   119 |   120 |   121 |   122 |   123 |   124 |   125 |   126 |   127 |  
"use strict";
128 |  
129 | module.exports = {
130 |   up: (queryInterface, Sequelize) => {
131 |     return queryInterface.createTable("users", {
132 |       id: {
133 |         type: Sequelize.INTEGER,
134 |         primaryKey: true,
135 |         autoIncrement: true,
136 |         allowNull: false
137 |       },
138 |       name: {
139 |         type: Sequelize.STRING,
140 |         allowNull: false
141 |       },
142 |       email: {
143 |         type: Sequelize.STRING,
144 |         unique: true,
145 |         allowNull: false
146 |       },
147 |       password_hash: {
148 |         type: Sequelize.STRING,
149 |         allowNull: false
150 |       },
151 |       created_at: {
152 |         type: Sequelize.DATE,
153 |         allowNull: false
154 |       },
155 |       updated_at: {
156 |         type: Sequelize.DATE,
157 |         allowNull: false
158 |       }
159 |     });
160 |   },
161 |  
162 |   down: (queryInterface, Sequelize) => {
163 |     return queryInterface.dropTable("users");
164 |   }
165 | };
166 |  
168 |
169 |
170 | 174 | 175 | 176 | 183 | 184 | 185 | 186 | 187 | -------------------------------------------------------------------------------- /__tests__/coverage/lcov-report/src/database/migrations/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Code coverage report for src/database/migrations 5 | 6 | 7 | 8 | 9 | 14 | 15 | 16 |
17 |
18 |

19 | All files src/database/migrations 20 |

21 |
22 |
23 | 0% 24 | Statements 25 | 0/3 26 |
27 |
28 | 100% 29 | Branches 30 | 0/0 31 |
32 |
33 | 0% 34 | Functions 35 | 0/2 36 |
37 |
38 | 0% 39 | Lines 40 | 0/3 41 |
42 |
43 |

44 | Press n or j to go to the next uncovered block, b, p or k for the previous block. 45 |

46 |
47 |
48 |
49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 |
FileStatementsBranchesFunctionsLines
20190219195005-create-users.js
0%0/3100%0/00%0/20%0/3
79 |
80 |
81 | 85 | 86 | 87 | 94 | 95 | 96 | 97 | 98 | -------------------------------------------------------------------------------- /__tests__/coverage/lcov-report/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Code coverage report for src 5 | 6 | 7 | 8 | 9 | 14 | 15 | 16 |
17 |
18 |

19 | All files src 20 |

21 |
22 |
23 | 88.89% 24 | Statements 25 | 16/18 26 |
27 |
28 | 25% 29 | Branches 30 | 1/4 31 |
32 |
33 | 100% 34 | Functions 35 | 4/4 36 |
37 |
38 | 88.89% 39 | Lines 40 | 16/18 41 |
42 |
43 |

44 | Press n or j to go to the next uncovered block, b, p or k for the previous block. 45 |

46 |
47 |
48 |
49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 |
FileStatementsBranchesFunctionsLines
app.js
100%8/850%1/2100%3/3100%8/8
routes.js
100%8/8100%0/0100%1/1100%8/8
server.js
0%0/20%0/2100%0/00%0/2
105 |
106 |
107 | 111 | 112 | 113 | 120 | 121 | 122 | 123 | 124 | -------------------------------------------------------------------------------- /__tests__/coverage/lcov-report/src/routes.js.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Code coverage report for src/routes.js 5 | 6 | 7 | 8 | 9 | 14 | 15 | 16 |
17 |
18 |

19 | All files / src routes.js 20 |

21 |
22 |
23 | 100% 24 | Statements 25 | 8/8 26 |
27 |
28 | 100% 29 | Branches 30 | 0/0 31 |
32 |
33 | 100% 34 | Functions 35 | 1/1 36 |
37 |
38 | 100% 39 | Lines 40 | 8/8 41 |
42 |
43 |

44 | Press n or j to go to the next uncovered block, b, p or k for the previous block. 45 |

46 |
47 |
48 |

 49 | 
 95 | 
1 50 | 2 51 | 3 52 | 4 53 | 5 54 | 6 55 | 7 56 | 8 57 | 9 58 | 10 59 | 11 60 | 12 61 | 13 62 | 14 63 | 15 64 | 161x 65 |   66 | 1x 67 |   68 | 1x 69 |   70 | 1x 71 |   72 | 1x 73 |   74 | 1x 75 | 1x 76 |   77 |   78 | 1x 79 |  
const routes = require("express").Router();
 80 |  
 81 | const authMiddleware = require("./app/middleware/auth");
 82 |  
 83 | const SessionController = require("./app/controllers/SessionController");
 84 |  
 85 | routes.post("/sessions", SessionController.store);
 86 |  
 87 | routes.use(authMiddleware);
 88 |  
 89 | routes.get("/dashboard", (req, res) => {
 90 |   return res.status(200).send();
 91 | });
 92 |  
 93 | module.exports = routes;
 94 |  
96 |
97 |
98 | 102 | 103 | 104 | 111 | 112 | 113 | 114 | 115 | -------------------------------------------------------------------------------- /__tests__/coverage/lcov-report/src/server.js.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Code coverage report for src/server.js 5 | 6 | 7 | 8 | 9 | 14 | 15 | 16 |
17 |
18 |

19 | All files / src server.js 20 |

21 |
22 |
23 | 0% 24 | Statements 25 | 0/2 26 |
27 |
28 | 0% 29 | Branches 30 | 0/2 31 |
32 |
33 | 100% 34 | Functions 35 | 0/0 36 |
37 |
38 | 0% 39 | Lines 40 | 0/2 41 |
42 |
43 |

44 | Press n or j to go to the next uncovered block, b, p or k for the previous block. 45 |

46 |
47 |
48 |

49 | 
59 | 
1 50 | 2 51 | 3 52 | 4  53 |   54 |   55 |  
const app = require("./app");
56 |  
57 | app.listen(process.env.PORT || 3000);
58 |  
60 |
61 |
62 | 66 | 67 | 68 | 75 | 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /__tests__/coverage/lcov.info: -------------------------------------------------------------------------------- 1 | TN: 2 | SF:/Users/diegofernandes/www/masterclass/authtdd/src/app.js 3 | FN:8,(anonymous_0) 4 | FN:15,(anonymous_1) 5 | FN:19,(anonymous_2) 6 | FNF:3 7 | FNH:3 8 | FNDA:1,(anonymous_0) 9 | FNDA:1,(anonymous_1) 10 | FNDA:1,(anonymous_2) 11 | DA:1,1 12 | DA:5,1 13 | DA:9,1 14 | DA:11,1 15 | DA:12,1 16 | DA:16,1 17 | DA:20,1 18 | DA:24,1 19 | LF:8 20 | LH:8 21 | BRDA:2,0,0,1 22 | BRDA:2,0,1,0 23 | BRF:2 24 | BRH:1 25 | end_of_record 26 | TN: 27 | SF:/Users/diegofernandes/www/masterclass/authtdd/src/routes.js 28 | FN:11,(anonymous_0) 29 | FNF:1 30 | FNH:1 31 | FNDA:1,(anonymous_0) 32 | DA:1,1 33 | DA:3,1 34 | DA:5,1 35 | DA:7,1 36 | DA:9,1 37 | DA:11,1 38 | DA:12,1 39 | DA:15,1 40 | LF:8 41 | LH:8 42 | BRF:0 43 | BRH:0 44 | end_of_record 45 | TN: 46 | SF:/Users/diegofernandes/www/masterclass/authtdd/src/server.js 47 | FNF:0 48 | FNH:0 49 | DA:1,0 50 | DA:3,0 51 | LF:2 52 | LH:0 53 | BRDA:3,0,0,0 54 | BRDA:3,0,1,0 55 | BRF:2 56 | BRH:0 57 | end_of_record 58 | TN: 59 | SF:/Users/diegofernandes/www/masterclass/authtdd/src/app/controllers/SessionController.js 60 | FN:4,(anonymous_0) 61 | FNF:1 62 | FNH:1 63 | FNDA:3,(anonymous_0) 64 | DA:1,1 65 | DA:5,3 66 | DA:7,3 67 | DA:9,3 68 | DA:10,0 69 | DA:13,3 70 | DA:14,1 71 | DA:17,2 72 | DA:24,1 73 | LF:9 74 | LH:8 75 | BRDA:9,0,0,0 76 | BRDA:9,0,1,3 77 | BRDA:13,1,0,1 78 | BRDA:13,1,1,2 79 | BRF:4 80 | BRH:3 81 | end_of_record 82 | TN: 83 | SF:/Users/diegofernandes/www/masterclass/authtdd/src/app/middleware/auth.js 84 | FN:4,(anonymous_0) 85 | FNF:1 86 | FNH:1 87 | FNDA:3,(anonymous_0) 88 | DA:1,1 89 | DA:2,1 90 | DA:4,1 91 | DA:5,3 92 | DA:7,3 93 | DA:8,1 94 | DA:11,2 95 | DA:13,2 96 | DA:14,2 97 | DA:16,1 98 | DA:18,1 99 | DA:20,1 100 | LF:12 101 | LH:12 102 | BRDA:7,0,0,1 103 | BRDA:7,0,1,2 104 | BRF:2 105 | BRH:2 106 | end_of_record 107 | TN: 108 | SF:/Users/diegofernandes/www/masterclass/authtdd/src/app/models/User.js 109 | FN:4,(anonymous_0) 110 | FN:15,(anonymous_1) 111 | FN:24,(anonymous_2) 112 | FN:28,(anonymous_3) 113 | FNF:4 114 | FNH:4 115 | FNDA:2,(anonymous_0) 116 | FNDA:5,(anonymous_1) 117 | FNDA:3,(anonymous_2) 118 | FNDA:3,(anonymous_3) 119 | DA:1,2 120 | DA:2,2 121 | DA:4,2 122 | DA:5,2 123 | DA:16,5 124 | DA:17,5 125 | DA:24,2 126 | DA:25,3 127 | DA:28,2 128 | DA:29,3 129 | DA:32,2 130 | LF:11 131 | LH:11 132 | BRDA:16,0,0,5 133 | BRDA:16,0,1,0 134 | BRF:2 135 | BRH:1 136 | end_of_record 137 | TN: 138 | SF:/Users/diegofernandes/www/masterclass/authtdd/src/app/models/index.js 139 | FN:18,(anonymous_0) 140 | FN:23,(anonymous_1) 141 | FN:28,(anonymous_2) 142 | FNF:3 143 | FNH:3 144 | FNDA:4,(anonymous_0) 145 | FNDA:2,(anonymous_1) 146 | FNDA:2,(anonymous_2) 147 | DA:3,2 148 | DA:4,2 149 | DA:5,2 150 | DA:6,2 151 | DA:7,2 152 | DA:8,2 153 | DA:10,2 154 | DA:17,2 155 | DA:19,4 156 | DA:24,2 157 | DA:25,2 158 | DA:28,2 159 | DA:29,2 160 | DA:30,0 161 | DA:34,2 162 | DA:35,2 163 | DA:37,2 164 | LF:17 165 | LH:16 166 | BRDA:20,0,0,4 167 | BRDA:20,0,1,4 168 | BRDA:20,0,2,2 169 | BRDA:29,1,0,0 170 | BRDA:29,1,1,2 171 | BRF:5 172 | BRH:4 173 | end_of_record 174 | TN: 175 | SF:/Users/diegofernandes/www/masterclass/authtdd/src/config/database.js 176 | FNF:0 177 | FNH:0 178 | DA:1,2 179 | DA:5,2 180 | LF:2 181 | LH:2 182 | BRDA:2,0,0,2 183 | BRDA:2,0,1,0 184 | BRDA:10,1,0,2 185 | BRDA:10,1,1,0 186 | BRF:4 187 | BRH:2 188 | end_of_record 189 | TN: 190 | SF:/Users/diegofernandes/www/masterclass/authtdd/src/database/migrations/20190219195005-create-users.js 191 | FN:4,(anonymous_0) 192 | FN:36,(anonymous_1) 193 | FNF:2 194 | FNH:0 195 | FNDA:0,(anonymous_0) 196 | FNDA:0,(anonymous_1) 197 | DA:3,0 198 | DA:5,0 199 | DA:37,0 200 | LF:3 201 | LH:0 202 | BRF:0 203 | BRH:0 204 | end_of_record 205 | -------------------------------------------------------------------------------- /__tests__/database.sqlite: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rocketseat-content/youtube-nodejs-tdd-jest/122d3dbc880e86f0890784a736ae7d933e0c4634/__tests__/database.sqlite -------------------------------------------------------------------------------- /__tests__/factories.js: -------------------------------------------------------------------------------- 1 | const faker = require("faker"); 2 | const { factory } = require("factory-girl"); 3 | const { User } = require("../src/app/models"); 4 | 5 | factory.define("User", User, { 6 | name: faker.name.findName(), 7 | email: faker.internet.email(), 8 | password: faker.internet.password() 9 | }); 10 | 11 | module.exports = factory; 12 | -------------------------------------------------------------------------------- /__tests__/integration/session.test.js: -------------------------------------------------------------------------------- 1 | const request = require("supertest"); 2 | 3 | const app = require("../../src/app"); 4 | const truncate = require("../utils/truncate"); 5 | const factory = require("../factories"); 6 | 7 | describe("Authentication", () => { 8 | beforeEach(async () => { 9 | await truncate(); 10 | }); 11 | 12 | it("should authenticate with valid credentials", async () => { 13 | const user = await factory.create("User", { 14 | password: "123123" 15 | }); 16 | 17 | const response = await request(app) 18 | .post("/sessions") 19 | .send({ 20 | email: user.email, 21 | password: "123123" 22 | }); 23 | 24 | expect(response.status).toBe(200); 25 | }); 26 | 27 | it("should not authenticate with invalid credentials", async () => { 28 | const user = await factory.create("User", { 29 | password: "123123" 30 | }); 31 | 32 | const response = await request(app) 33 | .post("/sessions") 34 | .send({ 35 | email: user.email, 36 | password: "123456" 37 | }); 38 | 39 | expect(response.status).toBe(401); 40 | }); 41 | 42 | it("should return jwt token when authenticated", async () => { 43 | const user = await factory.create("User", { 44 | password: "123123" 45 | }); 46 | 47 | const response = await request(app) 48 | .post("/sessions") 49 | .send({ 50 | email: user.email, 51 | password: "123123" 52 | }); 53 | 54 | expect(response.body).toHaveProperty("token"); 55 | }); 56 | 57 | it("should be able to access private routes when authenticated", async () => { 58 | const user = await factory.create("User", { 59 | password: "123123" 60 | }); 61 | 62 | const response = await request(app) 63 | .get("/dashboard") 64 | .set("Authorization", `Bearer ${user.generateToken()}`); 65 | 66 | expect(response.status).toBe(200); 67 | }); 68 | 69 | it("should not be able to access private routes without jwt token", async () => { 70 | const response = await request(app).get("/dashboard"); 71 | 72 | expect(response.status).toBe(401); 73 | }); 74 | 75 | it("should not be able to access private routes with invalid jwt token", async () => { 76 | const response = await request(app) 77 | .get("/dashboard") 78 | .set("Authorization", `Bearer 123123`); 79 | 80 | expect(response.status).toBe(401); 81 | }); 82 | }); 83 | -------------------------------------------------------------------------------- /__tests__/unit/user.test.js: -------------------------------------------------------------------------------- 1 | const bcrypt = require("bcryptjs"); 2 | 3 | const { User } = require("../../src/app/models"); 4 | const truncate = require("../utils/truncate"); 5 | 6 | describe("User", () => { 7 | beforeEach(async () => { 8 | await truncate(); 9 | }); 10 | 11 | it("should encrypt user password", async () => { 12 | const user = await User.create({ 13 | name: "Diego", 14 | email: "diego@rocketseat.com.br", 15 | password: "123456" 16 | }); 17 | 18 | const compareHash = await bcrypt.compare("123456", user.password_hash); 19 | 20 | expect(compareHash).toBe(true); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /__tests__/utils/truncate.js: -------------------------------------------------------------------------------- 1 | const { sequelize } = require("../../src/app/models"); 2 | 3 | module.exports = () => { 4 | return Promise.all( 5 | Object.keys(sequelize.models).map(key => { 6 | return sequelize.models[key].destroy({ truncate: true, force: true }); 7 | }) 8 | ); 9 | }; 10 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | // For a detailed explanation regarding each configuration property, visit: 2 | // https://jestjs.io/docs/en/configuration.html 3 | 4 | module.exports = { 5 | // All imported modules in your tests should be mocked automatically 6 | // automock: false, 7 | 8 | // Stop running tests after `n` failures 9 | bail: true, 10 | 11 | // Respect "browser" field in package.json when resolving modules 12 | // browser: false, 13 | 14 | // The directory where Jest should store its cached dependency information 15 | // cacheDirectory: "/private/var/folders/m7/3yjbp5ds77xb33vt96mwlwq00000gn/T/jest_dx", 16 | 17 | // Automatically clear mock calls and instances between every test 18 | clearMocks: true, 19 | 20 | // Indicates whether the coverage information should be collected while executing the test 21 | collectCoverage: true, 22 | 23 | // An array of glob patterns indicating a set of files for which coverage information should be collected 24 | collectCoverageFrom: ["src/**"], 25 | 26 | // The directory where Jest should output its coverage files 27 | coverageDirectory: "__tests__/coverage", 28 | 29 | // An array of regexp pattern strings used to skip coverage collection 30 | // coveragePathIgnorePatterns: [ 31 | // "/node_modules/" 32 | // ], 33 | 34 | // A list of reporter names that Jest uses when writing coverage reports 35 | // coverageReporters: [ 36 | // "json", 37 | // "text", 38 | // "lcov", 39 | // "clover" 40 | // ], 41 | 42 | // An object that configures minimum threshold enforcement for coverage results 43 | // coverageThreshold: null, 44 | 45 | // A path to a custom dependency extractor 46 | // dependencyExtractor: null, 47 | 48 | // Make calling deprecated APIs throw helpful error messages 49 | // errorOnDeprecated: false, 50 | 51 | // Force coverage collection from ignored files usin a array of glob patterns 52 | // forceCoverageMatch: [], 53 | 54 | // A path to a module which exports an async function that is triggered once before all test suites 55 | // globalSetup: null, 56 | 57 | // A path to a module which exports an async function that is triggered once after all test suites 58 | // globalTeardown: null, 59 | 60 | // A set of global variables that need to be available in all test environments 61 | // globals: {}, 62 | 63 | // An array of directory names to be searched recursively up from the requiring module's location 64 | // moduleDirectories: [ 65 | // "node_modules" 66 | // ], 67 | 68 | // An array of file extensions your modules use 69 | // moduleFileExtensions: [ 70 | // "js", 71 | // "json", 72 | // "jsx", 73 | // "ts", 74 | // "tsx", 75 | // "node" 76 | // ], 77 | 78 | // A map from regular expressions to module names that allow to stub out resources with a single module 79 | // moduleNameMapper: {}, 80 | 81 | // An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader 82 | // modulePathIgnorePatterns: [], 83 | 84 | // Activates notifications for test results 85 | // notify: false, 86 | 87 | // An enum that specifies notification mode. Requires { notify: true } 88 | // notifyMode: "failure-change", 89 | 90 | // A preset that is used as a base for Jest's configuration 91 | // preset: null, 92 | 93 | // Run tests from one or more projects 94 | // projects: null, 95 | 96 | // Use this configuration option to add custom reporters to Jest 97 | // reporters: undefined, 98 | 99 | // Automatically reset mock state between every test 100 | // resetMocks: false, 101 | 102 | // Reset the module registry before running each individual test 103 | // resetModules: false, 104 | 105 | // A path to a custom resolver 106 | // resolver: null, 107 | 108 | // Automatically restore mock state between every test 109 | // restoreMocks: false, 110 | 111 | // The root directory that Jest should scan for tests and modules within 112 | // rootDir: null, 113 | 114 | // A list of paths to directories that Jest should use to search for files in 115 | // roots: [ 116 | // "" 117 | // ], 118 | 119 | // Allows you to use a custom runner instead of Jest's default test runner 120 | // runner: "jest-runner", 121 | 122 | // The paths to modules that run some code to configure or set up the testing environment before each test 123 | // setupFiles: [], 124 | 125 | // A list of paths to modules that run some code to configure or set up the testing framework before each test 126 | // setupFilesAfterEnv: [], 127 | 128 | // A list of paths to snapshot serializer modules Jest should use for snapshot testing 129 | // snapshotSerializers: [], 130 | 131 | // The test environment that will be used for testing 132 | testEnvironment: "node", 133 | 134 | // Options that will be passed to the testEnvironment 135 | // testEnvironmentOptions: {}, 136 | 137 | // Adds a location field to test results 138 | // testLocationInResults: false, 139 | 140 | // The glob patterns Jest uses to detect test files 141 | testMatch: ["**/__tests__/**/*.test.js?(x)"] 142 | 143 | // An array of regexp pattern strings that are matched against all test paths, matched tests are skipped 144 | // testPathIgnorePatterns: [ 145 | // "/node_modules/" 146 | // ], 147 | 148 | // The regexp pattern or array of patterns that Jest uses to detect test files 149 | // testRegex: [], 150 | 151 | // This option allows the use of a custom results processor 152 | // testResultsProcessor: null, 153 | 154 | // This option allows use of a custom test runner 155 | // testRunner: "jasmine2", 156 | 157 | // This option sets the URL for the jsdom environment. It is reflected in properties such as location.href 158 | // testURL: "http://localhost", 159 | 160 | // Setting this value to "fake" allows the use of fake timers for functions such as "setTimeout" 161 | // timers: "real", 162 | 163 | // A map from regular expressions to paths to transformers 164 | // transform: null, 165 | 166 | // An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation 167 | // transformIgnorePatterns: [ 168 | // "/node_modules/" 169 | // ], 170 | 171 | // An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them 172 | // unmockedModulePathPatterns: undefined, 173 | 174 | // Indicates whether each individual test should be reported during the run 175 | // verbose: null, 176 | 177 | // An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode 178 | // watchPathIgnorePatterns: [], 179 | 180 | // Whether to use watchman for file crawling 181 | // watchman: true, 182 | }; 183 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "authtdd", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "license": "MIT", 6 | "scripts": { 7 | "start": "node src/server.js", 8 | "dev": "nodemon src/server.js --ignore __tests__", 9 | "pretest": "NODE_ENV=test sequelize db:migrate", 10 | "test": "NODE_ENV=test jest", 11 | "posttest": "NODE_ENV=test sequelize db:migrate:undo:all" 12 | }, 13 | "dependencies": { 14 | "bcryptjs": "^2.4.3", 15 | "dotenv": "^6.2.0", 16 | "express": "^4.16.4", 17 | "factory-girl": "^5.0.4", 18 | "faker": "^4.1.0", 19 | "jsonwebtoken": "^8.5.0", 20 | "pg": "^7.8.0", 21 | "sequelize": "^4.42.0" 22 | }, 23 | "devDependencies": { 24 | "jest": "^24.1.0", 25 | "nodemon": "^1.18.10", 26 | "sequelize-cli": "^5.4.0", 27 | "sqlite3": "^4.0.6", 28 | "supertest": "^3.4.2" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/app.js: -------------------------------------------------------------------------------- 1 | require("dotenv").config({ 2 | path: process.env.NODE_ENV === "test" ? ".env.test" : ".env" 3 | }); 4 | 5 | const express = require("express"); 6 | 7 | class AppController { 8 | constructor() { 9 | this.express = express(); 10 | 11 | this.middlewares(); 12 | this.routes(); 13 | } 14 | 15 | middlewares() { 16 | this.express.use(express.json()); 17 | } 18 | 19 | routes() { 20 | this.express.use(require("./routes")); 21 | } 22 | } 23 | 24 | module.exports = new AppController().express; 25 | -------------------------------------------------------------------------------- /src/app/controllers/SessionController.js: -------------------------------------------------------------------------------- 1 | const { User } = require("../models"); 2 | 3 | class SessionController { 4 | async store(req, res) { 5 | const { email, password } = req.body; 6 | 7 | const user = await User.findOne({ where: { email } }); 8 | 9 | if (!user) { 10 | return res.status(401).json({ message: "User not found" }); 11 | } 12 | 13 | if (!(await user.checkPassword(password))) { 14 | return res.status(401).json({ message: "Incorrect password" }); 15 | } 16 | 17 | return res.json({ 18 | user, 19 | token: user.generateToken() 20 | }); 21 | } 22 | } 23 | 24 | module.exports = new SessionController(); 25 | -------------------------------------------------------------------------------- /src/app/middleware/auth.js: -------------------------------------------------------------------------------- 1 | const jwt = require("jsonwebtoken"); 2 | const { promisify } = require("util"); 3 | 4 | module.exports = async (req, res, next) => { 5 | const authHeader = req.headers.authorization; 6 | 7 | if (!authHeader) { 8 | return res.status(401).json({ message: "Token not provided" }); 9 | } 10 | 11 | const [, token] = authHeader.split(" "); 12 | 13 | try { 14 | const decoded = await promisify(jwt.verify)(token, process.env.APP_SECRET); 15 | 16 | req.userId = decoded.id; 17 | 18 | return next(); 19 | } catch (err) { 20 | return res.status(401).json({ message: "Token invalid" }); 21 | } 22 | }; 23 | -------------------------------------------------------------------------------- /src/app/models/User.js: -------------------------------------------------------------------------------- 1 | const bcrypt = require("bcryptjs"); 2 | const jwt = require("jsonwebtoken"); 3 | 4 | module.exports = (sequelize, DataTypes) => { 5 | const User = sequelize.define( 6 | "User", 7 | { 8 | name: DataTypes.STRING, 9 | email: DataTypes.STRING, 10 | password: DataTypes.VIRTUAL, 11 | password_hash: DataTypes.STRING 12 | }, 13 | { 14 | hooks: { 15 | beforeSave: async user => { 16 | if (user.password) { 17 | user.password_hash = await bcrypt.hash(user.password, 8); 18 | } 19 | } 20 | } 21 | } 22 | ); 23 | 24 | User.prototype.checkPassword = function(password) { 25 | return bcrypt.compare(password, this.password_hash); 26 | }; 27 | 28 | User.prototype.generateToken = function() { 29 | return jwt.sign({ id: this.id }, process.env.APP_SECRET); 30 | }; 31 | 32 | return User; 33 | }; 34 | -------------------------------------------------------------------------------- /src/app/models/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const fs = require("fs"); 4 | const path = require("path"); 5 | const Sequelize = require("sequelize"); 6 | const basename = path.basename(__filename); 7 | const config = require("../../config/database"); 8 | const db = {}; 9 | 10 | const sequelize = new Sequelize( 11 | config.database, 12 | config.username, 13 | config.password, 14 | config 15 | ); 16 | 17 | fs.readdirSync(__dirname) 18 | .filter(file => { 19 | return ( 20 | file.indexOf(".") !== 0 && file !== basename && file.slice(-3) === ".js" 21 | ); 22 | }) 23 | .forEach(file => { 24 | const model = sequelize["import"](path.join(__dirname, file)); 25 | db[model.name] = model; 26 | }); 27 | 28 | Object.keys(db).forEach(modelName => { 29 | if (db[modelName].associate) { 30 | db[modelName].associate(db); 31 | } 32 | }); 33 | 34 | db.sequelize = sequelize; 35 | db.Sequelize = Sequelize; 36 | 37 | module.exports = db; 38 | -------------------------------------------------------------------------------- /src/config/database.js: -------------------------------------------------------------------------------- 1 | require("dotenv").config({ 2 | path: process.env.NODE_ENV === "test" ? ".env.test" : ".env" 3 | }); 4 | 5 | module.exports = { 6 | host: process.env.DB_HOST, 7 | username: process.env.DB_USER, 8 | password: process.env.DB_PASS, 9 | database: process.env.DB_NAME, 10 | dialect: process.env.DB_DIALECT || "postgres", 11 | storage: "./__tests__/database.sqlite", 12 | operatorsAliases: false, 13 | logging: false, 14 | define: { 15 | timestamps: true, 16 | underscored: true, 17 | underscoredAll: true 18 | } 19 | }; 20 | -------------------------------------------------------------------------------- /src/database/migrations/20190219195005-create-users.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | module.exports = { 4 | up: (queryInterface, Sequelize) => { 5 | return queryInterface.createTable("users", { 6 | id: { 7 | type: Sequelize.INTEGER, 8 | primaryKey: true, 9 | autoIncrement: true, 10 | allowNull: false 11 | }, 12 | name: { 13 | type: Sequelize.STRING, 14 | allowNull: false 15 | }, 16 | email: { 17 | type: Sequelize.STRING, 18 | unique: true, 19 | allowNull: false 20 | }, 21 | password_hash: { 22 | type: Sequelize.STRING, 23 | allowNull: false 24 | }, 25 | created_at: { 26 | type: Sequelize.DATE, 27 | allowNull: false 28 | }, 29 | updated_at: { 30 | type: Sequelize.DATE, 31 | allowNull: false 32 | } 33 | }); 34 | }, 35 | 36 | down: (queryInterface, Sequelize) => { 37 | return queryInterface.dropTable("users"); 38 | } 39 | }; 40 | -------------------------------------------------------------------------------- /src/routes.js: -------------------------------------------------------------------------------- 1 | const routes = require("express").Router(); 2 | 3 | const authMiddleware = require("./app/middleware/auth"); 4 | 5 | const SessionController = require("./app/controllers/SessionController"); 6 | 7 | routes.post("/sessions", SessionController.store); 8 | 9 | routes.use(authMiddleware); 10 | 11 | routes.get("/dashboard", (req, res) => { 12 | return res.status(200).send(); 13 | }); 14 | 15 | module.exports = routes; 16 | -------------------------------------------------------------------------------- /src/server.js: -------------------------------------------------------------------------------- 1 | const app = require("./app"); 2 | 3 | app.listen(process.env.PORT || 3000); 4 | --------------------------------------------------------------------------------