Timestamp | 15 |Status | 16 |Details | 17 |
---|---|---|
{{ log.timestamp | date: 'HH:mm:ss' }} | 22 |23 | 24 | | 25 |26 | |
├── .editorconfig ├── .gitignore ├── .sailsrc ├── Gruntfile.js ├── README.md ├── api ├── controllers │ ├── .gitkeep │ ├── AdminController.js │ ├── AuthorController.js │ ├── CategoryController.js │ ├── ExceptionController.js │ ├── FileController.js │ ├── ProjectController.js │ ├── ReportController.js │ ├── SearchController.js │ ├── SettingsController.js │ ├── TestController.js │ └── UserController.js ├── models │ ├── .gitkeep │ ├── Author.js │ ├── Category.js │ ├── Environment.js │ ├── Exception.js │ ├── Log.js │ ├── Media.js │ ├── Project.js │ ├── Report.js │ ├── Settings.js │ ├── Test.js │ └── User.js ├── policies │ └── sessionAuth.js ├── responses │ ├── badRequest.js │ ├── created.js │ ├── forbidden.js │ ├── notFound.js │ ├── ok.js │ └── serverError.js └── services │ ├── .gitkeep │ └── FileService.js ├── app.js ├── assets ├── favicon.ico ├── images │ └── .gitkeep ├── js │ ├── api │ │ ├── ExtentX.js │ │ ├── controllers │ │ │ ├── AdminController.js │ │ │ ├── AnalysisController.js │ │ │ ├── AuthorController.js │ │ │ ├── CategoryController.js │ │ │ ├── DataPointsController.js │ │ │ ├── ExceptionController.js │ │ │ ├── HeaderController.js │ │ │ ├── ModalInstanceController.js │ │ │ ├── NavigationController.js │ │ │ ├── ProjectController.js │ │ │ ├── ReportController.js │ │ │ ├── SearchController.js │ │ │ ├── TestController.js │ │ │ └── UserController.js │ │ ├── directives │ │ │ ├── analysisViewConfig.js │ │ │ ├── reportCategoryView.js │ │ │ ├── reportDetailsView.js │ │ │ └── reportExceptionView.js │ │ └── services │ │ │ ├── BarChartSettings.js │ │ │ ├── CSRFToken.js │ │ │ ├── ChartSettings.js │ │ │ ├── DataPointFormat.js │ │ │ ├── Icon.js │ │ │ ├── LineChartSettings.js │ │ │ ├── PieChartSettings.js │ │ │ └── ViewNameSetter.js │ └── dependencies │ │ ├── angular-1.5.8.min.js │ │ ├── angular-animate-1.5.8.min.js │ │ ├── angular-charts-1.0.3.min.js │ │ ├── angular-cookies-1.5.8.min.js │ │ ├── angular-route-1.5.8.min.js │ │ ├── angular-ui-bootstrap-tpls-2.1.4.min.js │ │ ├── chartjs-2.3.0.min.js │ │ ├── featherlight-1.5.0.min.js │ │ ├── jquery-2.2.4.min.js │ │ └── sails.io.js ├── partials │ ├── admin.html │ ├── analysis.html │ ├── author-summary.html │ ├── category-summary.html │ ├── change-password.html │ ├── exception-summary.html │ ├── index.html │ ├── node-template.html │ ├── projects-overview.html │ ├── report-list.html │ ├── report-summary.html │ ├── report-template-blocks.html │ ├── report-template.html │ ├── report.html │ ├── search.html │ ├── test-history-template.html │ ├── test-template-history.html │ ├── test-template.html │ └── test.html ├── robots.txt ├── styles │ ├── css.css │ └── dependencies │ │ ├── bootstrap-3.3.7-min.css │ │ ├── featherlight-1.5.0.min.css │ │ ├── icons │ │ ├── MaterialIcons-Regular.eot │ │ ├── MaterialIcons-Regular.ijmap │ │ ├── MaterialIcons-Regular.svg │ │ ├── MaterialIcons-Regular.ttf │ │ ├── MaterialIcons-Regular.woff │ │ └── MaterialIcons-Regular.woff2 │ │ └── material-icons.css └── templates │ └── .gitkeep ├── config ├── blueprints.js ├── bootstrap.js ├── connections.js ├── cors.js ├── csrf.js ├── env │ ├── development.js │ └── production.js ├── globals.js ├── http.js ├── i18n.js ├── locales │ ├── _README.md │ ├── de.json │ ├── en.json │ ├── es.json │ └── fr.json ├── log.js ├── models.js ├── policies.js ├── routes.js ├── session.js ├── sockets.js └── views.js ├── license ├── package.json ├── tasks ├── README.md ├── config │ ├── clean.js │ ├── coffee.js │ ├── concat.js │ ├── copy.js │ ├── cssmin.js │ ├── jst.js │ ├── less.js │ ├── sails-linker.js │ ├── sync.js │ ├── uglify.js │ └── watch.js ├── pipeline.js └── register │ ├── build.js │ ├── buildProd.js │ ├── compileAssets.js │ ├── default.js │ ├── linkAssets.js │ ├── linkAssetsBuild.js │ ├── linkAssetsBuildProd.js │ ├── prod.js │ └── syncAssets.js └── views ├── 403.ejs ├── 404.ejs ├── 500.ejs ├── layout.ejs └── partials ├── dataPointsSetting.ejs ├── header.ejs ├── sidenav.ejs └── signon.ejs /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ################################################ 2 | ############### .gitignore ################## 3 | ################################################ 4 | # 5 | # This file is only relevant if you are using git. 6 | # 7 | # Files which match the splat patterns below will 8 | # be ignored by git. This keeps random crap and 9 | # sensitive credentials from being uploaded to 10 | # your repository. It allows you to configure your 11 | # app for your machine without accidentally 12 | # committing settings which will smash the local 13 | # settings of other developers on your team. 14 | # 15 | # Some reasonable defaults are included below, 16 | # but, of course, you should modify/extend/prune 17 | # to fit your needs! 18 | ################################################ 19 | 20 | 21 | 22 | 23 | ################################################ 24 | # Local Configuration 25 | # 26 | # Explicitly ignore files which contain: 27 | # 28 | # 1. Sensitive information you'd rather not push to 29 | # your git repository. 30 | # e.g., your personal API keys or passwords. 31 | # 32 | # 2. Environment-specific configuration 33 | # Basically, anything that would be annoying 34 | # to have to change every time you do a 35 | # `git pull` 36 | # e.g., your local development database, or 37 | # the S3 bucket you're using for file uploads 38 | # development. 39 | # 40 | ################################################ 41 | 42 | config/local.js 43 | 44 | 45 | 46 | 47 | 48 | ################################################ 49 | # Dependencies 50 | # 51 | # When releasing a production app, you may 52 | # consider including your node_modules and 53 | # bower_components directory in your git repo, 54 | # but during development, its best to exclude it, 55 | # since different developers may be working on 56 | # different kernels, where dependencies would 57 | # need to be recompiled anyway. 58 | # 59 | # More on that here about node_modules dir: 60 | # http://www.futurealoof.com/posts/nodemodules-in-git.html 61 | # (credit Mikeal Rogers, @mikeal) 62 | # 63 | # About bower_components dir, you can see this: 64 | # http://addyosmani.com/blog/checking-in-front-end-dependencies/ 65 | # (credit Addy Osmani, @addyosmani) 66 | # 67 | ################################################ 68 | 69 | node_modules 70 | bower_components 71 | 72 | 73 | 74 | 75 | ################################################ 76 | # Sails.js / Waterline / Grunt 77 | # 78 | # Files generated by Sails and Grunt, or related 79 | # tasks and adapters. 80 | ################################################ 81 | .tmp 82 | dump.rdb 83 | 84 | 85 | 86 | 87 | 88 | ################################################ 89 | # Node.js / NPM 90 | # 91 | # Common files generated by Node, NPM, and the 92 | # related ecosystem. 93 | ################################################ 94 | lib-cov 95 | *.seed 96 | *.log 97 | *.out 98 | *.pid 99 | npm-debug.log 100 | 101 | 102 | 103 | 104 | 105 | ################################################ 106 | # Miscellaneous 107 | # 108 | # Common files generated by text editors, 109 | # operating systems, file systems, etc. 110 | ################################################ 111 | 112 | *~ 113 | *# 114 | .DS_STORE 115 | .netbeans 116 | nbproject 117 | .idea 118 | .node_history 119 | -------------------------------------------------------------------------------- /.sailsrc: -------------------------------------------------------------------------------- 1 | { 2 | "generators": { 3 | "modules": {} 4 | } 5 | } -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Gruntfile 3 | * 4 | * This Node script is executed when you run `grunt` or `sails lift`. 5 | * It's purpose is to load the Grunt tasks in your project's `tasks` 6 | * folder, and allow you to add and remove tasks as you see fit. 7 | * For more information on how this works, check out the `README.md` 8 | * file that was generated in your `tasks` folder. 9 | * 10 | * WARNING: 11 | * Unless you know what you're doing, you shouldn't change this file. 12 | * Check out the `tasks` directory instead. 13 | */ 14 | 15 | module.exports = function(grunt) { 16 | 17 | 18 | // Load the include-all library in order to require all of our grunt 19 | // configurations and task registrations dynamically. 20 | var includeAll; 21 | try { 22 | includeAll = require('include-all'); 23 | } catch (e0) { 24 | try { 25 | includeAll = require('sails/node_modules/include-all'); 26 | } catch (e1) { 27 | console.error('Could not find `include-all` module.'); 28 | console.error('Skipping grunt tasks...'); 29 | console.error('To fix this, please run:'); 30 | console.error('npm install include-all --save`'); 31 | console.error(); 32 | 33 | grunt.registerTask('default', []); 34 | return; 35 | } 36 | } 37 | 38 | 39 | /** 40 | * Loads Grunt configuration modules from the specified 41 | * relative path. These modules should export a function 42 | * that, when run, should either load/configure or register 43 | * a Grunt task. 44 | */ 45 | function loadTasks(relPath) { 46 | return includeAll({ 47 | dirname: require('path').resolve(__dirname, relPath), 48 | filter: /(.+)\.js$/, 49 | excludeDirs: /^\.(git|svn)$/ 50 | }) || {}; 51 | } 52 | 53 | /** 54 | * Invokes the function from a Grunt configuration module with 55 | * a single argument - the `grunt` object. 56 | */ 57 | function invokeConfigFn(tasks) { 58 | for (var taskName in tasks) { 59 | if (tasks.hasOwnProperty(taskName)) { 60 | tasks[taskName](grunt); 61 | } 62 | } 63 | } 64 | 65 | 66 | 67 | // Load task functions 68 | var taskConfigurations = loadTasks('./tasks/config'), 69 | registerDefinitions = loadTasks('./tasks/register'); 70 | 71 | // (ensure that a default task exists) 72 | if (!registerDefinitions.default) { 73 | registerDefinitions.default = function(grunt) { 74 | grunt.registerTask('default', []); 75 | }; 76 | } 77 | 78 | // Run task functions to configure Grunt. 79 | invokeConfigFn(taskConfigurations); 80 | invokeConfigFn(registerDefinitions); 81 | 82 | }; 83 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ExtentX Community 1.0.2 2 | 3 | Report Server for ExtentReports 4 | 5 | ### Supported ExtentReports Versions 6 | 7 | * ExtentReports Java Pro 3.0.0+ 8 | * ExtentReports Java Community 3.0.1+ 9 | * ExtentReports .NET Community 3.0.0+ 10 | 11 | ### Download 12 | 13 | Download from [extentreports.com](http://extentreports.com/community/) 14 | -------------------------------------------------------------------------------- /api/controllers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anshooarora/extentx/aa7b56a11b95df16c3f9343879112c92b1323eca/api/controllers/.gitkeep -------------------------------------------------------------------------------- /api/controllers/AdminController.js: -------------------------------------------------------------------------------- 1 | /** 2 | * AdminController 3 | * 4 | * @description :: Server-side logic for managing admin controls 5 | * @help :: See http://sailsjs.org/#!/documentation/concepts/Controllers 6 | */ 7 | 8 | var ObjectId = require('mongodb').ObjectID, 9 | _ = require('lodash'); 10 | 11 | module.exports = { 12 | 13 | deleteReportsOlderThanXDays: function(req, res) { 14 | var days = req.body.query, 15 | dt = new Date(), 16 | dt = dt.setDate(dt.getDate() - days + 1), 17 | dt = new Date(dt); 18 | console.log(dt) 19 | var reportObj = { 20 | reportIds: [], 21 | reportNames: [] 22 | }; 23 | 24 | function deleteRels() { 25 | if (reportObj.reportIds.length > 0) { 26 | Report.destroy({ id: reportObj.reportIds }).exec(function(err) { if (err) console.log(err); }); 27 | Author.destroy({ report: reportObj.reportIds }).exec(function(err) { if (err) console.log(err); }); 28 | Category.destroy({ report: reportObj.reportIds }).exec(function(err) { if (err) console.log(err); }); 29 | Log.destroy({ report: reportObj.reportIds }).exec(function(err) { if (err) console.log(err); }); 30 | Test.destroy({ report: reportObj.reportIds }).exec(function(err) { if (err) console.log(err); }); 31 | Exception.destroy({ report: reportObj.reportIds }).exec(function(err) { if (err) console.log(err); }); 32 | Media.destroy({ report: reportObj.reportIds }).exec(function(err) { if (err) console.log(err); }); 33 | 34 | res.json(reportObj); 35 | } 36 | else { 37 | console.log('No items match search criteria'); 38 | } 39 | } 40 | 41 | Report.find({ startTime: { '<': dt } }).exec(function(err, result) { 42 | _(result).forEach(function(element) { 43 | reportObj.reportIds.push(element.id); 44 | reportObj.reportNames.push(element.name); 45 | }); 46 | 47 | deleteRels(); 48 | }); 49 | }, 50 | 51 | resetDatabase: function(req, res) { 52 | try { 53 | Project.destroy({ }).exec(function(err) { if (err) console.log(err); }); 54 | Report.destroy({ }).exec(function(err) { if (err) console.log(err); }); 55 | Author.destroy({ }).exec(function(err) { if (err) console.log(err); }); 56 | Category.destroy({ }).exec(function(err) { if (err) console.log(err); }); 57 | Log.destroy({ }).exec(function(err) { if (err) console.log(err); }); 58 | Test.destroy({ }).exec(function(err) { if (err) console.log(err); }); 59 | Exception.destroy({ }).exec(function(err) { if (err) console.log(err); }); 60 | Media.destroy({ }).exec(function(err) { if (err) console.log(err); }); 61 | res.send(200); 62 | } catch(err) { 63 | res.send(400); 64 | } 65 | }, 66 | 67 | }; -------------------------------------------------------------------------------- /api/controllers/AuthorController.js: -------------------------------------------------------------------------------- 1 | /** 2 | * AuthorController 3 | * 4 | * @description :: Server-side logic for managing authors 5 | * @help :: See http://sailsjs.org/#!/documentation/concepts/Controllers 6 | */ 7 | 8 | var ObjectId = require('mongodb').ObjectID, 9 | _ = require('lodash'); 10 | 11 | module.exports = { 12 | 13 | getAuthorListByReportId: function(req, res) { 14 | var reportId = req.body.query.id; 15 | 16 | Report.findOne({ id: reportId }).populate('categories').exec(function(err, authorList) { 17 | if (err) res.json(null); 18 | else res.json(authorList); 19 | }) 20 | }, 21 | 22 | getTestsByAuthorId: function(req, res) { 23 | var catId = req.body.query.id; 24 | 25 | Author.findOne({ id: catId }).populate('tests').exec(function(err, authorTests) { 26 | if (err || typeof authorTests.tests === 'undefined') { 27 | res.json(null); 28 | } else { 29 | var testIds = []; 30 | _(authorTests.tests).forEach(function(test) { 31 | testIds.push(ObjectId(test.id)); 32 | }); 33 | 34 | Test.getTests({ id: testIds }, function(tests) { 35 | if (err) res.json(null); 36 | else res.json(tests); 37 | }); 38 | } 39 | }); 40 | }, 41 | 42 | getAuthorNamesWithTestCountsByProject: function(req, res) { 43 | var project = { $ne: null }; 44 | if (typeof req.session.project !== 'undefined' && req.session.project != null) 45 | project = req.session.project; 46 | 47 | Project.find({ name: project }).exec(function(err, projects) { 48 | if (projects.length && projects.length === 1) 49 | project = ObjectId(projects[0].id); 50 | 51 | Author.native(function(err, nativeColl) { 52 | nativeColl.aggregate( 53 | [ 54 | { 55 | $match: { 56 | $and: [ 57 | { project: project } 58 | ] 59 | } 60 | }, 61 | { $group: 62 | { 63 | _id: { name: "$name" }, 64 | count: { $sum: 1 }, 65 | }, 66 | }, 67 | { $sort : { count: -1 } } 68 | ], 69 | function(err, result) { 70 | if (err) console.log(err); 71 | res.json(result); 72 | }); 73 | }); 74 | }); 75 | }, 76 | 77 | getAuthorNamesWithFailedTestCountsByProject: function(req, res) { 78 | var project = { $ne: null }; 79 | if (typeof req.session.project !== 'undefined' && req.session.project != null) 80 | project = req.session.project; 81 | 82 | var dic = {}, 83 | cntr = 0; 84 | 85 | Project.find({ name: project }).exec(function(err, projects) { 86 | if (projects.length && projects.length === 1) 87 | project = ObjectId(projects[0].id); 88 | 89 | Author.find({ project: project }).populate("tests", { where: { status: { "!": "pass" }, level: 0 }}).exec(function(err, authorTests) { 90 | for (var ix = 0; ix < authorTests.length; ix++) { 91 | var author = authorTests[ix]; 92 | if (typeof dic[author.name] === "undefined") 93 | dic[author.name] = 0; 94 | 95 | dic[author.name] = dic[author.name] + author.tests.length; 96 | 97 | if (++cntr == authorTests.length) 98 | res.json(dic); 99 | } 100 | }); 101 | }); 102 | }, 103 | 104 | }; 105 | 106 | -------------------------------------------------------------------------------- /api/controllers/CategoryController.js: -------------------------------------------------------------------------------- 1 | /** 2 | * CategoryController 3 | * 4 | * @description :: Server-side logic for managing Categories 5 | * @help :: See http://sailsjs.org/#!/documentation/concepts/Controllers 6 | */ 7 | 8 | var ObjectId = require('mongodb').ObjectID, 9 | _ = require('lodash'); 10 | 11 | module.exports = { 12 | 13 | getDistinctCategoryNamesByProject: function(req, res) { 14 | var project = { $ne: null }; 15 | if (typeof req.session.project !== 'undefined' && req.session.project != null) 16 | project = req.session.project; 17 | 18 | Project.find({ name: project }).exec(function(err, projects) { 19 | if (projects.length && projects.length === 1) 20 | project = ObjectId(projects[0].id); 21 | 22 | Category.native(function(err, collection) { 23 | collection.aggregate([ 24 | { "$match": { "project": project } }, 25 | { "$group": { "_id": '$name' } } 26 | ], function(err, categories) { 27 | res.json(categories); 28 | }); 29 | }); 30 | }); 31 | }, 32 | 33 | getCategoryListByReportId: function(req, res) { 34 | var reportId = req.body.query.id; 35 | 36 | Report.findOne({ id: reportId }).populate('categories').exec(function(err, categoryList) { 37 | if (err) res.json(null); 38 | else res.json(categoryList); 39 | }) 40 | }, 41 | 42 | getTestsByCategoryId: function(req, res) { 43 | var catId = req.body.query.id; 44 | 45 | Category.findOne({ id: catId }).populate('tests').exec(function(err, categoryTests) { 46 | if (err || typeof categoryTests.tests === 'undefined') { 47 | res.json(null); 48 | } else { 49 | var testIds = []; 50 | _(categoryTests.tests).forEach(function(test) { 51 | testIds.push(ObjectId(test.id)); 52 | }); 53 | 54 | Test.getTests({ id: testIds }, function(tests) { 55 | if (err) res.json(null); 56 | else res.json(tests); 57 | }); 58 | } 59 | }); 60 | }, 61 | 62 | getCategoryNamesWithTestCountsByProject: function(req, res) { 63 | Category.getCategoryNamesWithTestCountsByProject(req, "", function(categoryTestCounts) { 64 | res.json(categoryTestCounts); 65 | }) 66 | }, 67 | 68 | getCategoryNamesWithFailedTestCountsByProject: function(req, res) { 69 | Category.getCategoryNamesWithTestCountsByProject(req, "pass", function(categoryTestCounts) { 70 | res.json(categoryTestCounts); 71 | }) 72 | }, 73 | 74 | }; 75 | 76 | -------------------------------------------------------------------------------- /api/controllers/ExceptionController.js: -------------------------------------------------------------------------------- 1 | /** 2 | * ExceptionController 3 | * 4 | * @description :: Server-side logic for managing Exceptions 5 | * @help :: See http://sailsjs.org/#!/documentation/concepts/Controllers 6 | */ 7 | 8 | var ObjectId = require('mongodb').ObjectID; 9 | 10 | module.exports = { 11 | 12 | getExceptionNamesWithTestCountsByProject: function(req, res) { 13 | var project = { $ne: null }; 14 | if (typeof req.session.project !== 'undefined' && req.session.project != null) 15 | project = req.session.project; 16 | 17 | Project.find({ name: project }).exec(function(err, projects) { 18 | if (projects.length && projects.length === 1) 19 | project = ObjectId(projects[0].id); 20 | 21 | Exception.native(function(err, nativeColl) { 22 | nativeColl.aggregate( 23 | [ 24 | { 25 | $match: { 26 | $and: [ 27 | { project: project } 28 | ] 29 | } 30 | }, 31 | { $group: 32 | { 33 | _id: { name: "$name" }, 34 | count: { $sum: "$testCount" }, 35 | }, 36 | }, 37 | { $sort : { count: -1 } } 38 | ], 39 | function(err, result) { 40 | if (err) console.log(err); 41 | res.json(result); 42 | }); 43 | }); 44 | }); 45 | }, 46 | 47 | getExceptionDistributionByReportByProject: function(req, res) { 48 | var project = { $ne: null }; 49 | if (typeof req.session.project !== 'undefined' && req.session.project != null) 50 | project = req.session.project; 51 | 52 | function sendRes(data) { 53 | res.json(data); 54 | } 55 | 56 | Project.find({ name: project }).exec(function(err, projects) { 57 | if (projects.length && projects.length === 1) 58 | project = ObjectId(projects[0].id); 59 | 60 | Report.find({ project: project }).sort({ startTime: 'desc' }).limit(10).populate("exceptions").exec(function(err, reportList) { 61 | if (err) console.log(err); 62 | 63 | var data = []; 64 | 65 | for (var ix = 0; ix < reportList.length; ix++) { 66 | var report = reportList[ix]; 67 | 68 | var exceptionCount = 0; 69 | for (var iy = 0; iy < report.exceptions.length; iy++) { 70 | var exception = report.exceptions[iy]; 71 | exceptionCount += exception.testCount; 72 | } 73 | 74 | data.push({ 75 | name: report.name, 76 | exceptionCount: exceptionCount 77 | }); 78 | 79 | var done = reportList.length-1 == ix; 80 | (done) && (sendRes(data)); 81 | } 82 | }) 83 | }); 84 | }, 85 | 86 | getExceptionsByReportId: function(req, res) { 87 | var reportId = req.body.query.reportId; 88 | 89 | Report.findOne({ id: reportId }).populate("exceptions").exec(function(err, exceptions) { 90 | res.json(exceptions); 91 | }) 92 | }, 93 | 94 | getTestsByExceptionId: function(req, res) { 95 | var id = req.body.query.id; 96 | 97 | Exception.findOne({ id: id }).populate("tests").exec(function(err, excceptionTests) { 98 | res.json(excceptionTests); 99 | }); 100 | }, 101 | 102 | }; 103 | 104 | -------------------------------------------------------------------------------- /api/controllers/FileController.js: -------------------------------------------------------------------------------- 1 | /** 2 | * FileController 3 | * 4 | * @description :: Server-side logic for managing Files 5 | * @help :: See http://sailsjs.org/#!/documentation/concepts/Controllers 6 | */ 7 | 8 | var ObjectId = require('mongodb').ObjectID; 9 | 10 | module.exports = { 11 | 12 | upload: function(req, res) { 13 | var reportId = req.body.reportId, 14 | testId = req.body.testId, 15 | logId = req.body.logId, 16 | id = req.body.id, 17 | name = req.body.name, 18 | mediaType = req.body.mediaType; 19 | 20 | if (typeof logId !== 'undefined') 21 | logId = ObjectId(logId); 22 | 23 | req.file('f').upload({}, function whenDone(err, uploadedFile) { 24 | if (err) { 25 | return res.negotiate(err); 26 | } 27 | 28 | // If no files were uploaded, respond with an error. 29 | if (uploadedFile.length === 0){ 30 | return res.badRequest('No file was uploaded'); 31 | } 32 | 33 | var targetPath = 'uploads/' + reportId + '/' + testId + '/' + name, 34 | movePath = '.tmp/public/' + targetPath; 35 | 36 | FileService.moveFile(uploadedFile[0].fd, movePath); 37 | 38 | Media.update( 39 | { id: id }, 40 | { 41 | path: targetPath, 42 | log: logId 43 | } 44 | ).exec(function afterwards(err, updated) { }); 45 | }); 46 | 47 | res.send(200); 48 | } 49 | 50 | }; 51 | 52 | -------------------------------------------------------------------------------- /api/controllers/ProjectController.js: -------------------------------------------------------------------------------- 1 | /** 2 | * ProjectController 3 | * 4 | * @description :: Server-side logic for managing Projects 5 | * @help :: See http://sailsjs.org/#!/documentation/concepts/Controllers 6 | */ 7 | 8 | module.exports = { 9 | 10 | switchProject: function(req, res) { 11 | req.session.project = null; 12 | 13 | var project = req.body.query.name; 14 | 15 | if (typeof project !== "undefined" && project !== null && project !== "") 16 | req.session.project = project; 17 | 18 | res.send(200); 19 | }, 20 | 21 | getProjects: function(req, res) { 22 | Project.find({}).exec(function(err, projects) { 23 | res.json(projects); 24 | }) 25 | }, 26 | 27 | getProjectsWithDeps: function(req, res) { 28 | Project.find({}).populateAll().exec(function(err, projectsInfo) { 29 | res.json(projectsInfo); 30 | }); 31 | }, 32 | 33 | destroyProjectWithDepsByProjectId: function(req, res) { 34 | var projectId = req.body.query.id; 35 | 36 | Project.findOne({ id: projectId }) 37 | .then(function(project) { 38 | Project.destroy({ id: projectId }).exec(function(err) {}); 39 | Report.destroy({ project: projectId }).exec(function(err) {}); 40 | Author.destroy({ project: projectId }).exec(function(err) {}); 41 | Category.destroy({ project: projectId }).exec(function(err) {}); 42 | Log.destroy({ project: projectId }).exec(function(err) {}); 43 | Test.destroy({ project: projectId }).exec(function(err) {}); 44 | Media.destroy({ project: projectId }).exec(function(err) {}); 45 | 46 | }) 47 | .catch(function(err) { 48 | console.log(err); 49 | }); 50 | 51 | res.send(200); 52 | }, 53 | 54 | }; 55 | 56 | -------------------------------------------------------------------------------- /api/controllers/SearchController.js: -------------------------------------------------------------------------------- 1 | /** 2 | * SearchController 3 | * 4 | * @description :: Server-side logic for managing Searches 5 | * @help :: See http://sailsjs.org/#!/documentation/concepts/Controllers 6 | */ 7 | 8 | module.exports = { 9 | 10 | searchTests: function(req, res) { 11 | var page = 1; 12 | if (typeof req.body.page !== "undefined") 13 | page = req.body.page; 14 | 15 | var startDate = typeof req.body.query.startDate === 'undefined' || req.body.query.startDate === '' 16 | ? new Date('01/01/1900') 17 | : new Date(req.body.query.startDate); 18 | 19 | var endDate = typeof req.body.query.endDate === 'undefined' || req.body.query.endDate === '' 20 | ? new Date() 21 | : new Date(req.body.query.endDate + ' 23:59:59'); 22 | 23 | var regex = req.body.query.regex; 24 | 25 | var name = typeof req.body.query.name === 'undefined' || req.body.query.name === '' 26 | ? '' 27 | : req.body.query.name; 28 | 29 | if (name !== '') { 30 | switch (regex) { 31 | case 'endsWith': 32 | name = { 'endsWith': name }; 33 | break; 34 | case 'startsWith': 35 | name = { 'startsWith': name }; 36 | break; 37 | case 'contains': 38 | name = { 'like': '%' + name + '%' }; 39 | break; 40 | default: 41 | break; 42 | } 43 | } else name = { $ne: null }; 44 | 45 | var status = typeof req.body.query.status === 'undefined' || req.body.query.status === '' || (req.body.query.status.length === 1 && req.body.query.status[0] === '') 46 | ? {$ne : null} 47 | : req.body.query.status; 48 | 49 | var categories = typeof req.body.query.category === 'undefined' || req.body.query.category === '' 50 | ? {$ne : null} 51 | : req.body.query.category; 52 | 53 | var project = { $ne: null }; 54 | if (typeof req.session.project !== 'undefined' && req.session.project != null) 55 | project = req.session.project; 56 | 57 | Project.find({ name: project }).exec(function(err, projects) { 58 | if (projects.length && projects.length === 1) project = projects[0].id; 59 | 60 | Test.find({ 61 | startTime: { '>=': startDate }, 62 | endTime: { '<=': endDate }, 63 | name: name, 64 | status: status 65 | }).paginate({ page: page, limit: 10 }).populateAll().exec(function(err, tests) { 66 | res.json(tests); 67 | }) 68 | }); 69 | }, 70 | 71 | }; 72 | 73 | -------------------------------------------------------------------------------- /api/controllers/SettingsController.js: -------------------------------------------------------------------------------- 1 | /** 2 | * SettingsController 3 | * 4 | * @description :: Server-side logic for managing Settings 5 | * @help :: See http://sailsjs.org/#!/documentation/concepts/Controllers 6 | */ 7 | 8 | module.exports = { 9 | 10 | updateDataPointSetting: function(req, res) { 11 | if (typeof req.body.query !== 'undefined') { 12 | var pts = req.body.query.dataPoints; 13 | if (typeof pts !== 'undefined') 14 | req.session.trendDataPoints = pts; 15 | 16 | var format = req.body.query.dataPointFormat; 17 | if (typeof format !== 'undefined') 18 | req.session.trendDataPointFormat = format; 19 | } 20 | 21 | res.send(200); 22 | }, 23 | 24 | setTheme: function(req, res) { 25 | var theme = req.body.query.theme; 26 | 27 | if (typeof req.session.theme !== 'undefined' && req.session.theme === theme) 28 | req.session.theme = ''; 29 | else 30 | req.session.theme = theme; 31 | 32 | res.send(200); 33 | }, 34 | 35 | }; 36 | 37 | -------------------------------------------------------------------------------- /api/controllers/TestController.js: -------------------------------------------------------------------------------- 1 | /** 2 | * TestController 3 | * 4 | * @description :: Server-side logic for managing Tests 5 | * @help :: See http://sailsjs.org/#!/documentation/concepts/Controllers 6 | */ 7 | 8 | var ObjectId = require('mongodb').ObjectID, 9 | _ = require("lodash"); 10 | 11 | module.exports = { 12 | 13 | /** 14 | * getTestHistory 15 | */ 16 | getTestHistory: function(req, res) { 17 | var id = req.body.query.id, 18 | name = req.body.query.name; 19 | 20 | Test.getTests({ name: name, id: { $ne: id }}, function(tests) { 21 | res.json(tests); 22 | }); 23 | }, 24 | 25 | /** 26 | * getTestById 27 | */ 28 | getTestById: function(req, res) { 29 | var id = req.body.query.id, 30 | cntr = 0; 31 | 32 | Test.findOne({ id: id }).populateAll().exec(function(err, test) { 33 | if (err) console.log('Test.getTestById -> ' + err); 34 | 35 | Test.deepPopulateLogElements(test, function(test) { 36 | Test.find({ parent: id }).populateAll().exec(function(err, nodes) { 37 | test.nodes = nodes; 38 | 39 | if (nodes.length === 0) 40 | res.json(test); 41 | 42 | for (var ix = 0; ix < nodes.length; ix++) { 43 | (function(ix) { 44 | Test.find({ parent: nodes[ix].id }).populateAll().exec(function(err, grandchildren) { 45 | test.nodes[ix].nodes = grandchildren; 46 | 47 | if (++cntr == nodes.length) 48 | res.json(test); 49 | }) 50 | })(ix) 51 | } 52 | }); 53 | }) 54 | }); 55 | }, 56 | 57 | getMostRecentTestByName: function(req, res) { 58 | var name = req.body.query.name; 59 | 60 | Test.findOne({ where: {name: name}, sort: 'startTime DESC' }).exec(function(err, test) { 61 | if (err) console.log(err); 62 | res.json(test); 63 | }) 64 | }, 65 | 66 | getTopFailedTestsByProject: function(req, res) { 67 | var project = { $ne: null }; 68 | if (typeof req.session.project !== 'undefined' && req.session.project != null) 69 | project = req.session.project; 70 | 71 | Project.find({ name: project }).exec(function(err, projects) { 72 | if (projects.length && projects.length === 1) project = projects[0].id; 73 | 74 | Report.find({ project: project }).sort({ startTime: 'desc' }).exec(function(err, reportList) { 75 | var reportIds = []; 76 | for (var ix = 0; ix < reportList.length; ix++) { 77 | reportIds.push(ObjectId(reportList[ix].id)); 78 | var done = reportList.length-1 == ix; 79 | (done) && (sendRes()); 80 | } 81 | 82 | function sendRes() { 83 | Test.getGroupsWithCounts([{ status: { $in: ['fail', 'fatal', 'error'] }}, { report: { $in: reportIds}}], { status: '$status', name: '$name' }, { count: -1 }, 10, function(topFailedTests) { 84 | res.json(topFailedTests); 85 | }); 86 | } 87 | }); 88 | }); 89 | }, 90 | 91 | }; 92 | 93 | -------------------------------------------------------------------------------- /api/controllers/UserController.js: -------------------------------------------------------------------------------- 1 | /** 2 | * UserController 3 | * 4 | * @description :: Server-side logic for managing Users 5 | * @help :: See http://sailsjs.org/#!/documentation/concepts/Controllers 6 | */ 7 | 8 | var bcrypt = require('bcryptjs'); 9 | 10 | module.exports = { 11 | 12 | signon: function(req, res) { 13 | var name = req.body.query.name; 14 | var password = req.body.query.password; 15 | 16 | User.findOne({ name: name }).exec(function(err, user) { 17 | if (err) throw (err); 18 | 19 | if (typeof user !== 'undefined' && typeof password !== 'undefined' && bcrypt.compareSync(password, user.password)) { 20 | req.session.user = user; 21 | req.session.authenticated = true; 22 | 23 | var user = req.session.user; 24 | delete user.password; 25 | 26 | res.send(200, { 27 | user: user 28 | }); 29 | } else { 30 | res.send(401); 31 | } 32 | }); 33 | }, 34 | 35 | isSignedOn: function(req, res) { 36 | if (req.session.authenticated) { 37 | var user = req.session.user; 38 | delete user.password; 39 | 40 | res.send(200, { user: user } ); 41 | } else { 42 | res.send(101); 43 | } 44 | }, 45 | 46 | /** 47 | * sign up for a user account 48 | */ 49 | signup: function(req, res) { 50 | var user = req.query.user; 51 | var password = req.query.password; 52 | 53 | User.find({ name: user }).exec(function(err, user) { 54 | if (err) throw (err); 55 | 56 | if (user.length > 0) { 57 | 58 | } 59 | else { 60 | var salt = bcrypt.genSaltSync(10); 61 | var hash = bcrypt.hashSync(password, salt); 62 | 63 | User.create({ 64 | name: user, 65 | password: hash 66 | }).exec(function(err, created) { 67 | if (err) throw (err); 68 | 69 | // signup success 70 | res.send(200); 71 | }); 72 | } 73 | }); 74 | }, 75 | 76 | logout: function(req, res) { 77 | req.session.destroy(); 78 | res.send(200); 79 | }, 80 | 81 | changePassword: function(req, res) { 82 | var userName = req.body.query.user, 83 | oldPassword = req.body.query.oldPassword, 84 | newPassword = req.body.query.newPassword; 85 | 86 | if (typeof userName !== 'undefined' && typeof oldPassword !== 'undefined' && typeof newPassword !== 'undefined') { 87 | User.findOne({ name: userName }).exec(function(err, user) { 88 | if (bcrypt.compareSync(oldPassword, user.password)) { 89 | var salt = bcrypt.genSaltSync(10); 90 | var hash = bcrypt.hashSync(newPassword, salt); 91 | 92 | User.update( 93 | { id: user.id }, 94 | { password: hash } 95 | ).exec(function() { 96 | res.send(200, { status: 'success', message: 'Password changed successfully' }); 97 | }) 98 | } else { 99 | res.send(400, { 100 | message: 'Current password is invalid' 101 | }) 102 | } 103 | }); 104 | } else { 105 | res.send(400, { 106 | message: 'Password is a required field, it cannot be undefined' 107 | }); 108 | } 109 | } 110 | 111 | }; 112 | 113 | -------------------------------------------------------------------------------- /api/models/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anshooarora/extentx/aa7b56a11b95df16c3f9343879112c92b1323eca/api/models/.gitkeep -------------------------------------------------------------------------------- /api/models/Author.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Author.js 3 | * 4 | * @description :: TODO: You might write a short summary of how this model works and what it represents here. 5 | * @docs :: http://sailsjs.org/#!documentation/models 6 | */ 7 | 8 | module.exports = { 9 | 10 | attributes: { 11 | /* tests (many) <-> authors (many) 12 | * tests and authors have a many to many relationship 13 | * - a test can be assigned with one or more authors 14 | * - a author can have one or more tests 15 | * In Waterline, from the Author model, to find tests: 16 | * Author.find().populate('tests').. 17 | */ 18 | tests: { 19 | collection: 'test', 20 | via: 'authors' 21 | }, 22 | 23 | project: { 24 | model: 'project' 25 | }, 26 | 27 | /* Owner 28 | * A report can have one or more authors 29 | * There is a one-to-many relationship between report and author 30 | * In Waterline, from the Report model, it is possible to do: 31 | * Report.find().populate('authors').. 32 | */ 33 | report: { 34 | model: 'report' 35 | }, 36 | 37 | testName: 'string', 38 | 39 | name: 'string', 40 | status: 'string' 41 | }, 42 | 43 | getDistinctNames: function(cb) { 44 | Author.native(function(err, collection) { 45 | collection.distinct('name', function(err, result) { 46 | if (err) console.log(err); 47 | else cb(result); 48 | }); 49 | }); 50 | }, 51 | }; 52 | 53 | -------------------------------------------------------------------------------- /api/models/Category.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Category.js 3 | * 4 | * @description :: TODO: You might write a short summary of how this model works and what it represents here. 5 | * @docs :: http://sailsjs.org/#!documentation/models 6 | */ 7 | 8 | var ObjectId = require('mongodb').ObjectID, 9 | _ = require("lodash"); 10 | 11 | module.exports = { 12 | 13 | attributes: { 14 | /* tests (many) <-> categories (many) 15 | * tests and categories have a many to many relationship 16 | * - a test can be assigned with one or more categories 17 | * - a category can have one or more tests 18 | * In Waterline, from the Category model, to find tests: 19 | * Category.find().populate('tests').. 20 | */ 21 | tests: { 22 | collection: 'test', 23 | via: 'categories' 24 | }, 25 | 26 | project: { 27 | model: 'project' 28 | }, 29 | 30 | /* Owner 31 | * A report can have one or more categories 32 | * There is a one-to-many relationship between report and category 33 | * In Waterline, from the Report model, it is possible to do: 34 | * Report.find().populate('categories').. 35 | */ 36 | report: { 37 | model: 'report' 38 | }, 39 | 40 | testName: 'string', 41 | 42 | name: 'string', 43 | status: 'string' 44 | }, 45 | 46 | getDistinctNames: function(cb) { 47 | Category.native(function(err, collection) { 48 | collection.distinct('name', function(err, result) { 49 | if (err) console.log(err); 50 | else cb(result); 51 | }); 52 | }); 53 | }, 54 | 55 | getGroupsWithCounts: function(matcher, groupBy, cb) { 56 | Category.native(function(err, collection) { 57 | collection.aggregate( 58 | [ 59 | { $match: matcher }, 60 | { $group: 61 | { 62 | _id: groupBy, 63 | count: { $sum: 1 }, 64 | }, 65 | } 66 | ], 67 | function(err, result) { 68 | if (err) console.log(err); 69 | else cb(result); 70 | }); 71 | }); 72 | }, 73 | 74 | getCategoryNamesWithTestCountsByProject: function(req, filterStatus, cb) { 75 | var project = { $ne: null }; 76 | if (typeof req.session.project !== 'undefined' && req.session.project != null) 77 | project = req.session.project; 78 | 79 | var dic = {}, 80 | cntr = 0; 81 | 82 | Project.find({ name: project }).exec(function(err, projects) { 83 | if (projects.length && projects.length === 1) 84 | project = ObjectId(projects[0].id); 85 | 86 | Category.find({ project: project }).populate("tests", { where: { status: { "!": filterStatus }, level: 0 }}).exec(function(err, categoryTests) { 87 | if (typeof categoryTests !== 'undefined' && categoryTests != null) { 88 | for (var ix = 0; ix < categoryTests.length; ix++) { 89 | var category = categoryTests[ix]; 90 | if (typeof dic[category.name] === "undefined") 91 | dic[category.name] = 0; 92 | 93 | dic[category.name] = dic[category.name] + category.tests.length; 94 | 95 | if (++cntr == categoryTests.length) 96 | cb(dic); 97 | } 98 | } else { 99 | cb ([]); 100 | } 101 | }); 102 | }); 103 | }, 104 | 105 | }; 106 | 107 | -------------------------------------------------------------------------------- /api/models/Environment.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Environment.js 3 | * 4 | * @description :: TODO: You might write a short summary of how this model works and what it represents here. 5 | * @docs :: http://sailsjs.org/#!documentation/models 6 | */ 7 | 8 | module.exports = { 9 | 10 | attributes: { 11 | project: { 12 | model: 'project' 13 | }, 14 | 15 | report: { 16 | model: 'report' 17 | }, 18 | 19 | name: 'string', 20 | value: 'string' 21 | } 22 | }; 23 | 24 | -------------------------------------------------------------------------------- /api/models/Exception.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Project.js 3 | * 4 | * @description :: TODO: You might write a short summary of how this model works and what it represents here. 5 | * @docs :: http://sailsjs.org/#!documentation/models 6 | */ 7 | 8 | module.exports = { 9 | 10 | attributes: { 11 | project: { 12 | model: 'project' 13 | }, 14 | 15 | report: { 16 | model: 'report' 17 | }, 18 | 19 | tests: { 20 | collection: 'test', 21 | via: 'exception' 22 | }, 23 | 24 | name: 'string', 25 | stacktrace: 'text', 26 | testCount: 'integer' 27 | }, 28 | 29 | }; 30 | 31 | -------------------------------------------------------------------------------- /api/models/Log.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Log.js 3 | * 4 | * @description :: TODO: You might write a short summary of how this model works and what it represents here. 5 | * @docs :: http://sailsjs.org/#!documentation/models 6 | */ 7 | 8 | module.exports = { 9 | 10 | attributes: { 11 | test: { 12 | model: 'test' 13 | }, 14 | 15 | project: { 16 | model: 'project' 17 | }, 18 | 19 | report: { 20 | model: 'report' 21 | }, 22 | 23 | media: { 24 | collection: 'media', 25 | via: 'log' 26 | }, 27 | 28 | testName: 'text', 29 | 30 | sequence: 'number', 31 | status: { 32 | type: 'string', 33 | enum: ['pass', 'fail', 'fatal', 'error', 'warning', 'skip', 'info'], 34 | defaultsTo: 'unknown' 35 | }, 36 | details: 'text', 37 | timestamp: 'date' 38 | }, 39 | 40 | getLogs: function(json, cb) { 41 | Log.find( 42 | json 43 | ).exec(function(err, result) { 44 | if (err) console.log('getLogs -> ' + err); 45 | 46 | cb(result); 47 | }); 48 | }, 49 | 50 | getGroupsWithCounts: function(matcher, groupBy, cb) { 51 | Log.native(function(err, collection) { 52 | collection.aggregate( 53 | [ 54 | { $match: matcher }, 55 | { $group: 56 | { 57 | _id: groupBy, 58 | count: { $sum: 1 }, 59 | }, 60 | } 61 | ], 62 | function(err, result) { 63 | if (err) console.log(err); 64 | else cb(result); 65 | }); 66 | }); 67 | }, 68 | }; 69 | 70 | -------------------------------------------------------------------------------- /api/models/Media.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Media.js 3 | * 4 | * @description :: TODO: You might write a short summary of how this model works and what it represents here. 5 | * @docs :: http://sailsjs.org/#!documentation/models 6 | */ 7 | 8 | module.exports = { 9 | 10 | attributes: { 11 | test: { 12 | model: 'test' 13 | }, 14 | 15 | project: { 16 | model: 'project' 17 | }, 18 | 19 | report: { 20 | model: 'report' 21 | }, 22 | 23 | log: { 24 | model: 'log' 25 | }, 26 | 27 | testName: 'text', 28 | 29 | sequence: 'number', 30 | path: 'string', 31 | mediaType: { 32 | type: 'string', 33 | enum: ['img', 'vid'] 34 | } 35 | }, 36 | 37 | }; 38 | 39 | -------------------------------------------------------------------------------- /api/models/Project.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Project.js 3 | * 4 | * @description :: TODO: You might write a short summary of how this model works and what it represents here. 5 | * @docs :: http://sailsjs.org/#!documentation/models 6 | */ 7 | 8 | module.exports = { 9 | 10 | attributes: { 11 | /* A project can have one or more reports 12 | * There is a one-to-many relationship between project and reports 13 | * In Waterline, from the Project model, it is possible to do: 14 | * Project.find().populate('reports').. 15 | * To find all reports of a given project, use: 16 | * Report.find({ project: projectId }).. 17 | */ 18 | reports: { 19 | collection: 'report', 20 | via: 'project' 21 | }, 22 | 23 | environment: { 24 | collection: 'environment', 25 | via: 'project' 26 | }, 27 | 28 | name: 'string' 29 | }, 30 | 31 | getProjects: function(cb) { 32 | Project.find().exec(function(err, res) { 33 | cb(res); 34 | }); 35 | }, 36 | }; 37 | 38 | -------------------------------------------------------------------------------- /api/models/Report.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Report.js 3 | * 4 | * @description :: TODO: You might write a short summary of how this model works and what it represents here. 5 | * @docs :: http://sailsjs.org/#!documentation/models 6 | */ 7 | 8 | var ObjectId = require('mongodb').ObjectID; 9 | 10 | module.exports = { 11 | 12 | attributes: { 13 | project: { 14 | model: 'project', 15 | }, 16 | 17 | /* A report can have one or more tests 18 | * There is a one-to-many relationship between report and test 19 | * In Waterline, from the Report model, it is possible to do: 20 | * Report.find().populate('tests').. 21 | */ 22 | tests: { 23 | collection: 'test', 24 | via: 'report' 25 | }, 26 | 27 | exceptions: { 28 | collection: 'exception', 29 | via: 'report' 30 | }, 31 | 32 | /* A report can have one or more categories 33 | * There is a one-to-many relationship between report and category 34 | */ 35 | categories: { 36 | collection: 'category', 37 | via: 'report' 38 | }, 39 | 40 | /* A report can have one or more authors 41 | * There is a one-to-many relationship between report and author 42 | */ 43 | authors: { 44 | collection: 'author', 45 | via: 'report' 46 | }, 47 | 48 | environment: { 49 | collection: 'environment', 50 | via: 'report' 51 | }, 52 | 53 | status: 'string', 54 | name: 'string', 55 | startTime: 'date', 56 | endTime: 'date', 57 | duration: 'integer', 58 | 59 | /* 60 | * stats 61 | */ 62 | parentLength: 'number', 63 | passParentLength: 'number', 64 | failParentLength: 'number', 65 | fatalParentLength: 'number', 66 | errorParentLength: 'number', 67 | warningParentLength: 'number', 68 | skipParentLength: 'number', 69 | exceptionsParentLength: 'number', 70 | 71 | childLength: 'number', 72 | passChildLength: 'number', 73 | failChildLength: 'number', 74 | fatalChildLength: 'number', 75 | errorChildLength: 'number', 76 | warningChildLength: 'number', 77 | skipChildLength: 'number', 78 | infoChildLength: 'number', 79 | exceptionsChildLength: 'number', 80 | 81 | grandChildLength: 'number', 82 | passGrandChildLength: 'number', 83 | failGrandChildLength: 'number', 84 | fatalGrandChildLength: 'number', 85 | errorGrandChildLength: 'number', 86 | warningGrandChildLength: 'number', 87 | skipGrandChildLength: 'number', 88 | infoGrandChildLength: 'number', 89 | exceptionsGrandChildLength: 'number', 90 | } 91 | }; 92 | 93 | -------------------------------------------------------------------------------- /api/models/Settings.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Settings.js 3 | * 4 | * @description :: TODO: You might write a short summary of how this model works and what it represents here. 5 | * @docs :: http://sailsjs.org/#!documentation/models 6 | */ 7 | 8 | module.exports = { 9 | 10 | attributes: { 11 | setting: 'string', 12 | value: 'string' 13 | }, 14 | }; 15 | 16 | -------------------------------------------------------------------------------- /api/models/User.js: -------------------------------------------------------------------------------- 1 | /** 2 | * User.js 3 | * 4 | * @description :: TODO: You might write a short summary of how this model works and what it represents here. 5 | * @docs :: http://sailsjs.org/#!documentation/models 6 | */ 7 | 8 | module.exports = { 9 | 10 | attributes: { 11 | name: { 12 | type: 'string', 13 | required: true, 14 | unique: true 15 | }, 16 | password: { 17 | type: 'string', 18 | required: true, 19 | unique: true 20 | }, 21 | lastLoggedIn: { 22 | type: 'date', 23 | required: true, 24 | defaultsTo: new Date(0) 25 | }, 26 | admin: { 27 | type: 'boolean', 28 | required: true, 29 | defaultsTo: false, 30 | }, 31 | role: { 32 | type: 'string' 33 | } 34 | }, 35 | } -------------------------------------------------------------------------------- /api/policies/sessionAuth.js: -------------------------------------------------------------------------------- 1 | /** 2 | * sessionAuth 3 | * 4 | * @module :: Policy 5 | * @description :: Simple policy to allow any authenticated user 6 | * Assumes that your login action in one of your controllers sets `req.session.authenticated = true;` 7 | * @docs :: http://sailsjs.org/#!/documentation/concepts/Policies 8 | * 9 | */ 10 | module.exports = function(req, res, next) { 11 | 12 | // User is allowed, proceed to the next policy, 13 | // or if this is the last policy, the controller 14 | if (req.session.authenticated) { 15 | return next(); 16 | } 17 | 18 | // User is not allowed 19 | // (default res.forbidden() behavior can be overridden in `config/403.js`) 20 | return res.forbidden('You are not permitted to perform this action.'); 21 | }; 22 | -------------------------------------------------------------------------------- /api/responses/badRequest.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 400 (Bad Request) Handler 3 | * 4 | * Usage: 5 | * return res.badRequest(); 6 | * return res.badRequest(data); 7 | * return res.badRequest(data, 'some/specific/badRequest/view'); 8 | * 9 | * e.g.: 10 | * ``` 11 | * return res.badRequest( 12 | * 'Please choose a valid `password` (6-12 characters)', 13 | * 'trial/signup' 14 | * ); 15 | * ``` 16 | */ 17 | 18 | module.exports = function badRequest(data, options) { 19 | 20 | // Get access to `req`, `res`, & `sails` 21 | var req = this.req; 22 | var res = this.res; 23 | var sails = req._sails; 24 | 25 | // Set status code 26 | res.status(400); 27 | 28 | // Log error to console 29 | if (data !== undefined) { 30 | sails.log.verbose('Sending 400 ("Bad Request") response: \n',data); 31 | } 32 | else sails.log.verbose('Sending 400 ("Bad Request") response'); 33 | 34 | // Only include errors in response if application environment 35 | // is not set to 'production'. In production, we shouldn't 36 | // send back any identifying information about errors. 37 | if (sails.config.environment === 'production' && sails.config.keepResponseErrors !== true) { 38 | data = undefined; 39 | } 40 | 41 | // If the user-agent wants JSON, always respond with JSON 42 | // If views are disabled, revert to json 43 | if (req.wantsJSON || sails.config.hooks.views === false) { 44 | return res.jsonx(data); 45 | } 46 | 47 | // If second argument is a string, we take that to mean it refers to a view. 48 | // If it was omitted, use an empty object (`{}`) 49 | options = (typeof options === 'string') ? { view: options } : options || {}; 50 | 51 | // Attempt to prettify data for views, if it's a non-error object 52 | var viewData = data; 53 | if (!(viewData instanceof Error) && 'object' == typeof viewData) { 54 | try { 55 | viewData = require('util').inspect(data, {depth: null}); 56 | } 57 | catch(e) { 58 | viewData = undefined; 59 | } 60 | } 61 | 62 | // If a view was provided in options, serve it. 63 | // Otherwise try to guess an appropriate view, or if that doesn't 64 | // work, just send JSON. 65 | if (options.view) { 66 | return res.view(options.view, { data: viewData, title: 'Bad Request' }); 67 | } 68 | 69 | // If no second argument provided, try to serve the implied view, 70 | // but fall back to sending JSON(P) if no view can be inferred. 71 | else return res.guessView({ data: viewData, title: 'Bad Request' }, function couldNotGuessView () { 72 | return res.jsonx(data); 73 | }); 74 | 75 | }; 76 | 77 | -------------------------------------------------------------------------------- /api/responses/created.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 201 (CREATED) Response 3 | * 4 | * Usage: 5 | * return res.created(); 6 | * return res.created(data); 7 | * return res.created(data, 'auth/login'); 8 | * 9 | * @param {Object} data 10 | * @param {String|Object} options 11 | * - pass string to render specified view 12 | */ 13 | 14 | module.exports = function created (data, options) { 15 | 16 | // Get access to `req`, `res`, & `sails` 17 | var req = this.req; 18 | var res = this.res; 19 | var sails = req._sails; 20 | 21 | sails.log.silly('res.created() :: Sending 201 ("CREATED") response'); 22 | 23 | // Set status code 24 | res.status(201); 25 | 26 | // If appropriate, serve data as JSON(P) 27 | // If views are disabled, revert to json 28 | if (req.wantsJSON || sails.config.hooks.views === false) { 29 | return res.jsonx(data); 30 | } 31 | 32 | // If second argument is a string, we take that to mean it refers to a view. 33 | // If it was omitted, use an empty object (`{}`) 34 | options = (typeof options === 'string') ? { view: options } : options || {}; 35 | 36 | // Attempt to prettify data for views, if it's a non-error object 37 | var viewData = data; 38 | if (!(viewData instanceof Error) && 'object' == typeof viewData) { 39 | try { 40 | viewData = require('util').inspect(data, {depth: null}); 41 | } 42 | catch(e) { 43 | viewData = undefined; 44 | } 45 | } 46 | 47 | // If a view was provided in options, serve it. 48 | // Otherwise try to guess an appropriate view, or if that doesn't 49 | // work, just send JSON. 50 | if (options.view) { 51 | return res.view(options.view, { data: viewData, title: 'Created' }); 52 | } 53 | 54 | // If no second argument provided, try to serve the implied view, 55 | // but fall back to sending JSON(P) if no view can be inferred. 56 | else return res.guessView({ data: viewData, title: 'Created' }, function couldNotGuessView () { 57 | return res.jsonx(data); 58 | }); 59 | 60 | }; 61 | -------------------------------------------------------------------------------- /api/responses/forbidden.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 403 (Forbidden) Handler 3 | * 4 | * Usage: 5 | * return res.forbidden(); 6 | * return res.forbidden(err); 7 | * return res.forbidden(err, 'some/specific/forbidden/view'); 8 | * 9 | * e.g.: 10 | * ``` 11 | * return res.forbidden('Access denied.'); 12 | * ``` 13 | */ 14 | 15 | module.exports = function forbidden (data, options) { 16 | 17 | // Get access to `req`, `res`, & `sails` 18 | var req = this.req; 19 | var res = this.res; 20 | var sails = req._sails; 21 | 22 | // Set status code 23 | res.status(403); 24 | 25 | // Log error to console 26 | if (data !== undefined) { 27 | sails.log.verbose('Sending 403 ("Forbidden") response: \n',data); 28 | } 29 | else sails.log.verbose('Sending 403 ("Forbidden") response'); 30 | 31 | // Only include errors in response if application environment 32 | // is not set to 'production'. In production, we shouldn't 33 | // send back any identifying information about errors. 34 | if (sails.config.environment === 'production' && sails.config.keepResponseErrors !== true) { 35 | data = undefined; 36 | } 37 | 38 | // If the user-agent wants JSON, always respond with JSON 39 | // If views are disabled, revert to json 40 | if (req.wantsJSON || sails.config.hooks.views === false) { 41 | return res.jsonx(data); 42 | } 43 | 44 | // If second argument is a string, we take that to mean it refers to a view. 45 | // If it was omitted, use an empty object (`{}`) 46 | options = (typeof options === 'string') ? { view: options } : options || {}; 47 | 48 | // Attempt to prettify data for views, if it's a non-error object 49 | var viewData = data; 50 | if (!(viewData instanceof Error) && 'object' == typeof viewData) { 51 | try { 52 | viewData = require('util').inspect(data, {depth: null}); 53 | } 54 | catch(e) { 55 | viewData = undefined; 56 | } 57 | } 58 | 59 | // If a view was provided in options, serve it. 60 | // Otherwise try to guess an appropriate view, or if that doesn't 61 | // work, just send JSON. 62 | if (options.view) { 63 | return res.view(options.view, { data: viewData, title: 'Forbidden' }); 64 | } 65 | 66 | // If no second argument provided, try to serve the default view, 67 | // but fall back to sending JSON(P) if any errors occur. 68 | else return res.view('403', { data: viewData, title: 'Forbidden' }, function (err, html) { 69 | 70 | // If a view error occured, fall back to JSON(P). 71 | if (err) { 72 | // 73 | // Additionally: 74 | // • If the view was missing, ignore the error but provide a verbose log. 75 | if (err.code === 'E_VIEW_FAILED') { 76 | sails.log.verbose('res.forbidden() :: Could not locate view for error page (sending JSON instead). Details: ',err); 77 | } 78 | // Otherwise, if this was a more serious error, log to the console with the details. 79 | else { 80 | sails.log.warn('res.forbidden() :: When attempting to render error page view, an error occured (sending JSON instead). Details: ', err); 81 | } 82 | return res.jsonx(data); 83 | } 84 | 85 | return res.send(html); 86 | }); 87 | 88 | }; 89 | 90 | -------------------------------------------------------------------------------- /api/responses/notFound.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 404 (Not Found) Handler 3 | * 4 | * Usage: 5 | * return res.notFound(); 6 | * return res.notFound(err); 7 | * return res.notFound(err, 'some/specific/notfound/view'); 8 | * 9 | * e.g.: 10 | * ``` 11 | * return res.notFound(); 12 | * ``` 13 | * 14 | * NOTE: 15 | * If a request doesn't match any explicit routes (i.e. `config/routes.js`) 16 | * or route blueprints (i.e. "shadow routes", Sails will call `res.notFound()` 17 | * automatically. 18 | */ 19 | 20 | module.exports = function notFound (data, options) { 21 | 22 | // Get access to `req`, `res`, & `sails` 23 | var req = this.req; 24 | var res = this.res; 25 | var sails = req._sails; 26 | 27 | // Set status code 28 | res.status(404); 29 | 30 | // Log error to console 31 | if (data !== undefined) { 32 | sails.log.verbose('Sending 404 ("Not Found") response: \n',data); 33 | } 34 | else sails.log.verbose('Sending 404 ("Not Found") response'); 35 | 36 | // Only include errors in response if application environment 37 | // is not set to 'production'. In production, we shouldn't 38 | // send back any identifying information about errors. 39 | if (sails.config.environment === 'production' && sails.config.keepResponseErrors !== true) { 40 | data = undefined; 41 | } 42 | 43 | // If the user-agent wants JSON, always respond with JSON 44 | // If views are disabled, revert to json 45 | if (req.wantsJSON || sails.config.hooks.views === false) { 46 | return res.jsonx(data); 47 | } 48 | 49 | // If second argument is a string, we take that to mean it refers to a view. 50 | // If it was omitted, use an empty object (`{}`) 51 | options = (typeof options === 'string') ? { view: options } : options || {}; 52 | 53 | // Attempt to prettify data for views, if it's a non-error object 54 | var viewData = data; 55 | if (!(viewData instanceof Error) && 'object' == typeof viewData) { 56 | try { 57 | viewData = require('util').inspect(data, {depth: null}); 58 | } 59 | catch(e) { 60 | viewData = undefined; 61 | } 62 | } 63 | 64 | // If a view was provided in options, serve it. 65 | // Otherwise try to guess an appropriate view, or if that doesn't 66 | // work, just send JSON. 67 | if (options.view) { 68 | return res.view(options.view, { data: viewData, title: 'Not Found' }); 69 | } 70 | 71 | // If no second argument provided, try to serve the default view, 72 | // but fall back to sending JSON(P) if any errors occur. 73 | else return res.view('404', { data: viewData, title: 'Not Found' }, function (err, html) { 74 | 75 | // If a view error occured, fall back to JSON(P). 76 | if (err) { 77 | // 78 | // Additionally: 79 | // • If the view was missing, ignore the error but provide a verbose log. 80 | if (err.code === 'E_VIEW_FAILED') { 81 | sails.log.verbose('res.notFound() :: Could not locate view for error page (sending JSON instead). Details: ',err); 82 | } 83 | // Otherwise, if this was a more serious error, log to the console with the details. 84 | else { 85 | sails.log.warn('res.notFound() :: When attempting to render error page view, an error occured (sending JSON instead). Details: ', err); 86 | } 87 | return res.jsonx(data); 88 | } 89 | 90 | return res.send(html); 91 | }); 92 | 93 | }; 94 | 95 | -------------------------------------------------------------------------------- /api/responses/ok.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 200 (OK) Response 3 | * 4 | * Usage: 5 | * return res.ok(); 6 | * return res.ok(data); 7 | * return res.ok(data, 'auth/login'); 8 | * 9 | * @param {Object} data 10 | * @param {String|Object} options 11 | * - pass string to render specified view 12 | */ 13 | 14 | module.exports = function sendOK (data, options) { 15 | 16 | // Get access to `req`, `res`, & `sails` 17 | var req = this.req; 18 | var res = this.res; 19 | var sails = req._sails; 20 | 21 | sails.log.silly('res.ok() :: Sending 200 ("OK") response'); 22 | 23 | // Set status code 24 | res.status(200); 25 | 26 | // If appropriate, serve data as JSON(P) 27 | // If views are disabled, revert to json 28 | if (req.wantsJSON || sails.config.hooks.views === false) { 29 | return res.jsonx(data); 30 | } 31 | 32 | // If second argument is a string, we take that to mean it refers to a view. 33 | // If it was omitted, use an empty object (`{}`) 34 | options = (typeof options === 'string') ? { view: options } : options || {}; 35 | 36 | // Attempt to prettify data for views, if it's a non-error object 37 | var viewData = data; 38 | if (!(viewData instanceof Error) && 'object' == typeof viewData) { 39 | try { 40 | viewData = require('util').inspect(data, {depth: null}); 41 | } 42 | catch(e) { 43 | viewData = undefined; 44 | } 45 | } 46 | 47 | // If a view was provided in options, serve it. 48 | // Otherwise try to guess an appropriate view, or if that doesn't 49 | // work, just send JSON. 50 | if (options.view) { 51 | return res.view(options.view, { data: viewData, title: 'OK' }); 52 | } 53 | 54 | // If no second argument provided, try to serve the implied view, 55 | // but fall back to sending JSON(P) if no view can be inferred. 56 | else return res.guessView({ data: viewData, title: 'OK' }, function couldNotGuessView () { 57 | return res.jsonx(data); 58 | }); 59 | 60 | }; 61 | -------------------------------------------------------------------------------- /api/responses/serverError.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 500 (Server Error) Response 3 | * 4 | * Usage: 5 | * return res.serverError(); 6 | * return res.serverError(err); 7 | * return res.serverError(err, 'some/specific/error/view'); 8 | * 9 | * NOTE: 10 | * If something throws in a policy or controller, or an internal 11 | * error is encountered, Sails will call `res.serverError()` 12 | * automatically. 13 | */ 14 | 15 | module.exports = function serverError (data, options) { 16 | 17 | // Get access to `req`, `res`, & `sails` 18 | var req = this.req; 19 | var res = this.res; 20 | var sails = req._sails; 21 | 22 | // Set status code 23 | res.status(500); 24 | 25 | // Log error to console 26 | if (data !== undefined) { 27 | sails.log.error('Sending 500 ("Server Error") response: \n',data); 28 | } 29 | else sails.log.error('Sending empty 500 ("Server Error") response'); 30 | 31 | // Only include errors in response if application environment 32 | // is not set to 'production'. In production, we shouldn't 33 | // send back any identifying information about errors. 34 | if (sails.config.environment === 'production' && sails.config.keepResponseErrors !== true) { 35 | data = undefined; 36 | } 37 | 38 | // If the user-agent wants JSON, always respond with JSON 39 | // If views are disabled, revert to json 40 | if (req.wantsJSON || sails.config.hooks.views === false) { 41 | return res.jsonx(data); 42 | } 43 | 44 | // If second argument is a string, we take that to mean it refers to a view. 45 | // If it was omitted, use an empty object (`{}`) 46 | options = (typeof options === 'string') ? { view: options } : options || {}; 47 | 48 | // Attempt to prettify data for views, if it's a non-error object 49 | var viewData = data; 50 | if (!(viewData instanceof Error) && 'object' == typeof viewData) { 51 | try { 52 | viewData = require('util').inspect(data, {depth: null}); 53 | } 54 | catch(e) { 55 | viewData = undefined; 56 | } 57 | } 58 | 59 | // If a view was provided in options, serve it. 60 | // Otherwise try to guess an appropriate view, or if that doesn't 61 | // work, just send JSON. 62 | if (options.view) { 63 | return res.view(options.view, { data: viewData, title: 'Server Error' }); 64 | } 65 | 66 | // If no second argument provided, try to serve the default view, 67 | // but fall back to sending JSON(P) if any errors occur. 68 | else return res.view('500', { data: viewData, title: 'Server Error' }, function (err, html) { 69 | 70 | // If a view error occured, fall back to JSON(P). 71 | if (err) { 72 | // 73 | // Additionally: 74 | // • If the view was missing, ignore the error but provide a verbose log. 75 | if (err.code === 'E_VIEW_FAILED') { 76 | sails.log.verbose('res.serverError() :: Could not locate view for error page (sending JSON instead). Details: ',err); 77 | } 78 | // Otherwise, if this was a more serious error, log to the console with the details. 79 | else { 80 | sails.log.warn('res.serverError() :: When attempting to render error page view, an error occured (sending JSON instead). Details: ', err); 81 | } 82 | return res.jsonx(data); 83 | } 84 | 85 | return res.send(html); 86 | }); 87 | 88 | }; 89 | 90 | -------------------------------------------------------------------------------- /api/services/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anshooarora/extentx/aa7b56a11b95df16c3f9343879112c92b1323eca/api/services/.gitkeep -------------------------------------------------------------------------------- /api/services/FileService.js: -------------------------------------------------------------------------------- 1 | /** 2 | * FileService 3 | * 4 | * @description :: file service 5 | */ 6 | 7 | var mv = require('mv'); 8 | 9 | module.exports = { 10 | moveFile: function(pathFrom, pathTo) { 11 | mv(pathFrom, pathTo, {mkdirp: true}, function(err) { 12 | if (err) console.log(err); 13 | }); 14 | }, 15 | }; -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | /** 2 | * app.js 3 | * 4 | * Use `app.js` to run your app without `sails lift`. 5 | * To start the server, run: `node app.js`. 6 | * 7 | * This is handy in situations where the sails CLI is not relevant or useful. 8 | * 9 | * For example: 10 | * => `node app.js` 11 | * => `forever start app.js` 12 | * => `node debug app.js` 13 | * => `modulus deploy` 14 | * => `heroku scale` 15 | * 16 | * 17 | * The same command-line arguments are supported, e.g.: 18 | * `node app.js --silent --port=80 --prod` 19 | */ 20 | 21 | // Ensure we're in the project directory, so relative paths work as expected 22 | // no matter where we actually lift from. 23 | process.chdir(__dirname); 24 | 25 | // Ensure a "sails" can be located: 26 | (function() { 27 | var sails; 28 | try { 29 | sails = require('sails'); 30 | } catch (e) { 31 | console.error('To run an app using `node app.js`, you usually need to have a version of `sails` installed in the same directory as your app.'); 32 | console.error('To do that, run `npm install sails`'); 33 | console.error(''); 34 | console.error('Alternatively, if you have sails installed globally (i.e. you did `npm install -g sails`), you can use `sails lift`.'); 35 | console.error('When you run `sails lift`, your app will still use a local `./node_modules/sails` dependency if it exists,'); 36 | console.error('but if it doesn\'t, the app will run with the global sails instead!'); 37 | return; 38 | } 39 | 40 | // Try to get `rc` dependency 41 | var rc; 42 | try { 43 | rc = require('rc'); 44 | } catch (e0) { 45 | try { 46 | rc = require('sails/node_modules/rc'); 47 | } catch (e1) { 48 | console.error('Could not find dependency: `rc`.'); 49 | console.error('Your `.sailsrc` file(s) will be ignored.'); 50 | console.error('To resolve this, run:'); 51 | console.error('npm install rc --save'); 52 | rc = function () { return {}; }; 53 | } 54 | } 55 | 56 | 57 | // Start server 58 | sails.lift(rc('sails')); 59 | })(); 60 | -------------------------------------------------------------------------------- /assets/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anshooarora/extentx/aa7b56a11b95df16c3f9343879112c92b1323eca/assets/favicon.ico -------------------------------------------------------------------------------- /assets/images/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anshooarora/extentx/aa7b56a11b95df16c3f9343879112c92b1323eca/assets/images/.gitkeep -------------------------------------------------------------------------------- /assets/js/api/ExtentX.js: -------------------------------------------------------------------------------- 1 | angular.module('ExtentX', ['ngRoute', 'ngCookies', 'chart.js', 'ui.bootstrap', 'ngAnimate']). 2 | config(['$routeProvider', '$locationProvider', function($routeProvider, $locationProvider) { 3 | $routeProvider. 4 | when('/', { templateUrl: 'partials/analysis.html' }). 5 | when('/report-list', { templateUrl: 'partials/report-list.html' }). 6 | when('/projects-overview', { templateUrl: 'partials/projects-overview.html' }). 7 | when('/report-summary', { templateUrl: 'partials/report-summary.html' }). 8 | when('/report', { templateUrl: 'partials/report.html' }). 9 | when('/test', { templateUrl: 'partials/test.html' }). 10 | when('/admin', { templateUrl: 'partials/admin.html' }). 11 | when('/change-password', { templateUrl: 'partials/change-password.html' }). 12 | when('/author-summary', { templateUrl: 'partials/author-summary.html' }). 13 | when('/category-summary', { templateUrl: 'partials/category-summary.html' }). 14 | when('/categories-by-report', { templateUrl: 'partials/categories-by-report.html' }). 15 | when('/exception-summary', { templateUrl: 'partials/exception-summary.html' }). 16 | when('/exceptions-by-report', { templateUrl: 'partials/exceptions-by-report.html' }). 17 | when('/search', { templateUrl: 'partials/search.html' }). 18 | otherwise({ redirectTo: '/' }); 19 | }]); 20 | -------------------------------------------------------------------------------- /assets/js/api/controllers/AdminController.js: -------------------------------------------------------------------------------- 1 | angular.module('ExtentX'). 2 | controller('AdminController', ['$scope', '$rootScope', '$http', '$window', function($scope, $rootScope, $http, $window) { 3 | $scope.resetComplete = false; 4 | 5 | $scope.archiveReport = function(reportId) { 6 | var currentPageTemplate = $route.current.templateUrl; 7 | $templateCache.remove(currentPageTemplate); 8 | $route.reload(); 9 | }; 10 | 11 | $scope.destroyReport = function(reportId) { 12 | var req = { 13 | method: 'POST', 14 | url: '/destroyReport', 15 | data: { 16 | query: reportId 17 | } 18 | }; 19 | 20 | $http.defaults.headers.post['X-CSRF-Token'] = $rootScope._csrf; 21 | 22 | $http(req). 23 | success(function(response) { 24 | $window.location.reload(); 25 | }). 26 | error(function(response) { 27 | }); 28 | }; 29 | 30 | $scope.deleteReportsOlderThanXDays = function(days) { 31 | if (days !== 'undefined' && typeof days !== 'undefined') { 32 | var req = { 33 | method: 'POST', 34 | url: '/deleteReportsOlderThanXDays', 35 | data: { 36 | query: days 37 | } 38 | }; 39 | 40 | $http.defaults.headers.post['X-CSRF-Token'] = $rootScope._csrf; 41 | 42 | $http(req). 43 | success(function(res) { 44 | $scope.deletedReports = res; 45 | }). 46 | error(function(res) { 47 | console.log(res); 48 | }); 49 | } 50 | }; 51 | 52 | $scope.resetDatabase = function() { 53 | var req = { 54 | method: 'POST', 55 | url: '/resetDatabase', 56 | }; 57 | 58 | $http.defaults.headers.post['X-CSRF-Token'] = $rootScope._csrf; 59 | 60 | $http(req). 61 | success(function(res) { 62 | $scope.resetComplete = true; 63 | }). 64 | error(function(res) { 65 | $scope.resetComplete = false; 66 | }); 67 | }; 68 | 69 | }]); -------------------------------------------------------------------------------- /assets/js/api/controllers/AuthorController.js: -------------------------------------------------------------------------------- 1 | angular.module('ExtentX') 2 | .controller('AuthorController', ['$rootScope', '$scope', '$http', '$location', 'Icon', 'ChartSettings', 'BarChartSettings', 'ViewNameSetter', 3 | function($rootScope, $scope, $http, $location, Icon, ChartSettings, BarChartSettings, ViewNameSetter) { 4 | var drawChart = false; 5 | $scope.showAuthorPanel = false; 6 | $scope.setViewName = ViewNameSetter.setViewName; 7 | 8 | $scope.getAuthorNamesWithTestCountsByProject = function(drawChart) { 9 | var req = { 10 | method: 'GET', 11 | url: '/getAuthorNamesWithTestCountsByProject' 12 | }; 13 | 14 | $http.defaults.headers.post['X-CSRF-Token'] = $rootScope._csrf; 15 | 16 | $http(req). 17 | success(function(res) { 18 | if (res.length > 0) 19 | $scope.showAuthorPanel = true; 20 | 21 | $scope.authorTestCounts = res; 22 | $scope.authorTestCountLength = Object.keys(res).length; 23 | 24 | if (drawChart) { 25 | $scope.options = BarChartSettings.options; 26 | $scope.chartWidth = document.getElementById("author-distribution-container").clientWidth - 40; 27 | 28 | $scope.authorDistributionLabels = [], 29 | $scope.authorDistributionData = []; 30 | 31 | for (var ix = 0; ix < res.length; ix++) { 32 | $scope.authorDistributionLabels.push(res[ix]._id.name); 33 | $scope.authorDistributionData.push(res[ix].count); 34 | } 35 | } 36 | }). 37 | error(function(err) { 38 | console.log(err); 39 | }); 40 | }; 41 | 42 | $scope.getAuthorNamesWithFailedTestCountsByProject = function() { 43 | var req = { 44 | method: 'GET', 45 | url: '/getAuthorNamesWithFailedTestCountsByProject' 46 | }; 47 | 48 | $http.defaults.headers.post['X-CSRF-Token'] = $rootScope._csrf; 49 | 50 | $http(req). 51 | success(function(res) { 52 | var labels = [], data = []; 53 | 54 | for (var prop in res) { 55 | labels.push(prop); 56 | data.push(res[prop]); 57 | } 58 | 59 | $scope.authorFailedTestDistributionLabels = labels; 60 | $scope.authorFailedTestDistributionData = data; 61 | }); 62 | }; 63 | 64 | $scope.getAuthorListByReportId = function(reportId) { 65 | var req = { 66 | method: 'POST', 67 | url: '/getAuthorListByReportId', 68 | data: { 69 | query: { 70 | id: reportId 71 | } 72 | } 73 | }; 74 | 75 | $http.defaults.headers.post['X-CSRF-Token'] = $rootScope._csrf; 76 | 77 | $http(req). 78 | success(function(res) { 79 | $scope.initial = true; 80 | $scope.reportName = res.name; 81 | $scope.authorList = res.authors; 82 | }); 83 | }; 84 | 85 | $scope.getTestsByAuthorId = function(authorId, authorName) { 86 | $scope.activeAuthor = authorName; 87 | 88 | var req = { 89 | method: 'POST', 90 | url: '/getTestsByAuthorId', 91 | data: { 92 | query: { 93 | id: authorId 94 | } 95 | } 96 | }; 97 | 98 | $http.defaults.headers.post['X-CSRF-Token'] = $rootScope._csrf; 99 | 100 | $http(req). 101 | success(function(res) { 102 | $scope.authorTests = res; 103 | }); 104 | }; 105 | }]); -------------------------------------------------------------------------------- /assets/js/api/controllers/DataPointsController.js: -------------------------------------------------------------------------------- 1 | angular.module('ExtentX'). 2 | controller('DataPointsController', ['$rootScope', '$scope', '$uibModal', '$sce', '$http', '$location', '$timeout', 'Icon', 3 | function($rootScope, $scope, $uibModal, $sce, $http, $location, $timeout, Icon) { 4 | 5 | $scope.updateDataPointSetting = function(query) { 6 | var req = { 7 | method: 'POST', 8 | url: '/updateDataPointSetting', 9 | data: { 10 | query: query 11 | } 12 | }; 13 | 14 | $http.defaults.headers.post['X-CSRF-Token'] = $rootScope._csrf; 15 | 16 | $http(req). 17 | success(function(response) { 18 | window.location.reload(); 19 | }). 20 | error(function(response) { 21 | if (typeof response !== 'undefined' && response != null) 22 | console.log(response); 23 | }); 24 | }; 25 | 26 | $scope.animationsEnabled = true; 27 | $scope.open = function (size) { 28 | var modalInstance = $uibModal.open({ 29 | animation: $scope.animationsEnabled, 30 | templateUrl: 'dataPointsSetting.html', 31 | controller: 'ModalInstanceController', 32 | size: 'sm', 33 | resolve: { 34 | items: function () { 35 | return $scope.items; 36 | } 37 | } 38 | }); 39 | 40 | $rootScope.modal = modalInstance; 41 | }; 42 | 43 | }]); -------------------------------------------------------------------------------- /assets/js/api/controllers/HeaderController.js: -------------------------------------------------------------------------------- 1 | angular.module('ExtentX'). 2 | controller('HeaderController', ['$rootScope', '$scope', function($rootScope, $scope) { 3 | $rootScope.viewName = "Analysis"; 4 | }]); -------------------------------------------------------------------------------- /assets/js/api/controllers/ModalInstanceController.js: -------------------------------------------------------------------------------- 1 | angular.module('ExtentX'). 2 | controller('ModalInstanceController', ['$scope', '$uibModalInstance', function($scope, $uibModalInstance) { 3 | $scope.ok = function () { 4 | $uibModalInstance.close(); 5 | }; 6 | 7 | $scope.cancel = function () { 8 | $uibModalInstance.dismiss('cancel'); 9 | }; 10 | }]); -------------------------------------------------------------------------------- /assets/js/api/controllers/NavigationController.js: -------------------------------------------------------------------------------- 1 | angular.module("ExtentX") 2 | .controller("NavigationController", ["$scope", "$rootScope", "$http", "$window", function($scope, $rootScope, $http, $window) { 3 | $scope.states = {}; 4 | $scope.states.activeItem = 'item1'; 5 | 6 | $scope.items = [{ 7 | id: "item1", 8 | target: "#/", 9 | title: "Analysis", 10 | icon: "dashboard" 11 | }, { 12 | id: "item2", 13 | target: "#/report-list", 14 | title: "Reports", 15 | icon: "folder_open" 16 | }, { 17 | id: "item3", 18 | target: "#/category-summary", 19 | title: "Category", 20 | icon: "local_offer" 21 | }, { 22 | id: "item4", 23 | target: "#/author-summary", 24 | title: "Author", 25 | icon: "person" 26 | }, { 27 | id: "item5", 28 | target: "#/exception-summary", 29 | title: "Exception", 30 | icon: "bug_report" 31 | }, { 32 | id: "item6", 33 | target: "#/search", 34 | title: "Search", 35 | icon: "search" 36 | }]; 37 | 38 | $scope.setTheme = function(theme) { 39 | var req = { 40 | method: 'POST', 41 | url: '/setTheme', 42 | data: { 43 | theme: theme 44 | } 45 | }; 46 | 47 | $http.defaults.headers.post['X-CSRF-Token'] = $rootScope._csrf; 48 | 49 | $http(req). 50 | success(function(response) { 51 | $window.location.reload(); 52 | }). 53 | error(function(response) { 54 | console.log(response); 55 | }); 56 | }; 57 | 58 | }]); -------------------------------------------------------------------------------- /assets/js/api/controllers/ProjectController.js: -------------------------------------------------------------------------------- 1 | angular.module('ExtentX') 2 | .controller('ProjectController', ['$rootScope', '$scope', '$http', '$location', 'Icon', 'ChartSettings', 'PieChartSettings', 3 | function($rootScope, $scope, $http, $location, Icon, ChartSettings, PieChartSettings) { 4 | $scope.projectAndDepsCleared = false; 5 | 6 | $scope.destroyProjectWithDepsByProjectId = function(projectId) { 7 | console.log(projectId) 8 | var req = { 9 | method: 'POST', 10 | url: '/destroyProjectWithDepsByProjectId', 11 | data: { 12 | query: { 13 | id: projectId 14 | } 15 | } 16 | }; 17 | 18 | $http.defaults.headers.post['X-CSRF-Token'] = $rootScope._csrf; 19 | 20 | $http(req). 21 | success(function(res) { $scope.projectAndDepsCleared = true; }); 22 | }; 23 | 24 | $scope.getProjectsWithDeps = function() { 25 | var req = { 26 | method: "GET", 27 | url: "/getProjectsWithDeps" 28 | }; 29 | 30 | $http.defaults.headers.post['X-CSRF-Token'] = $rootScope._csrf; 31 | 32 | $http(req). 33 | success(function(res) { 34 | $scope.projectsAndDeps = res; 35 | }). 36 | error(function(err) { 37 | console.log(err); 38 | }); 39 | }; 40 | 41 | $scope.loadProjects = function() { 42 | var req = { 43 | method: 'GET', 44 | url: '/getProjects' 45 | }; 46 | 47 | $http.defaults.headers.post['X-CSRF-Token'] = $rootScope._csrf; 48 | 49 | $http(req). 50 | success(function(res) { 51 | $scope.projects = res; 52 | }). 53 | error(function(err) { 54 | console.log(err); 55 | }); 56 | }; 57 | 58 | $scope.switchProject = function(name) { 59 | console.log(name) 60 | var req = { 61 | method: 'POST', 62 | url: '/switchProject', 63 | data: { 64 | query: { 65 | name: name 66 | } 67 | } 68 | }; 69 | 70 | $http.defaults.headers.post['X-CSRF-Token'] = $rootScope._csrf; 71 | 72 | $http(req). 73 | success(function(res) { 74 | window.location.reload(); 75 | }). 76 | error(function(err) { 77 | console.log(err); 78 | }); 79 | }; 80 | }]); -------------------------------------------------------------------------------- /assets/js/api/controllers/SearchController.js: -------------------------------------------------------------------------------- 1 | angular.module("ExtentX") 2 | .controller("SearchController", ["$scope", "$rootScope", "$http", "$window", '$uibModal', '$timeout', '$location', 3 | function($scope, $rootScope, $http, $window, $uibModal, $timeout, $location) { 4 | $scope.page = 1; 5 | $scope.dateFormat = 'MM-dd-yyyy'; 6 | $scope.startDatePicker = { opened: false }; 7 | $scope.endDatePicker = { opened: false }; 8 | $scope.openStartDate = function() { 9 | $scope.startDatePicker.opened = true; 10 | }; 11 | $scope.openEndDate = function() { 12 | $scope.endDatePicker.opened = true; 13 | }; 14 | 15 | $scope.getTimeDifference = function(to, from) { 16 | return DateTime.subtract(to, from); 17 | }; 18 | 19 | $scope.query = { 20 | regex: 'contains', 21 | }; 22 | 23 | $scope.searchTests = function(query, page) { 24 | if (page === 0) { 25 | $scope.page = 1; 26 | return; 27 | } 28 | 29 | $scope.page = page; 30 | 31 | var req = { 32 | method: 'POST', 33 | url: '/searchTests', 34 | data: { 35 | query: query, 36 | page: page 37 | } 38 | }; 39 | 40 | $http.defaults.headers.post['X-CSRF-Token'] = $rootScope._csrf; 41 | 42 | $http(req). 43 | success(function(res) { 44 | $scope.tests = res; 45 | }); 46 | }; 47 | 48 | /* if coming from another view with the search url, perform search dynamically 49 | * example of such a URL: 50 | * http://localhost:1337/#/search?regex=exact&name=testName 51 | */ 52 | var obj = $location.search(); 53 | if (Object.keys(obj).length !== 0 && JSON.stringify(obj) !== JSON.stringify({})) 54 | $scope.search(obj); 55 | }]); -------------------------------------------------------------------------------- /assets/js/api/controllers/TestController.js: -------------------------------------------------------------------------------- 1 | angular.module('ExtentX') 2 | .controller('TestController', ['$rootScope', '$scope', '$http', '$location', '$sce', 'Icon', 3 | function($rootScope, $scope, $http, $location, $sce, Icon) { 4 | $scope.path = '/test'; 5 | $scope.trust = $sce.trustAsHtml; 6 | $scope.activeTest = null; 7 | 8 | $scope.getIcon = function(status) { 9 | return Icon.getIcon(status); 10 | }; 11 | 12 | $scope.setActiveTest = function(test) { 13 | $scope.activeTest = test; 14 | }; 15 | 16 | $scope.$on("$destroy", function(){ 17 | angular.element(".navbar").removeClass("hidden"); 18 | }); 19 | 20 | $scope.loadTestById = function(testId, isHistorical) { 21 | var req = { 22 | method: 'POST', 23 | url: '/getTestById', 24 | data: { 25 | query: { 26 | id: testId 27 | } 28 | } 29 | }; 30 | 31 | $http.defaults.headers.post['X-CSRF-Token'] = $rootScope._csrf; 32 | 33 | $http(req). 34 | success(function(res) { 35 | if (!isHistorical) { 36 | $scope.test = res; 37 | $scope.historicalTest = null; 38 | $scope.historicalTestList = null; 39 | } 40 | else { 41 | $scope.historicalTest = res; 42 | } 43 | }); 44 | }; 45 | 46 | $scope.loadNodeById = function(testId, level, historical) { 47 | var req = { 48 | method: 'POST', 49 | url: '/getTestById', 50 | data: { 51 | query: { 52 | id: testId 53 | } 54 | } 55 | }; 56 | $http.defaults.headers.post['X-CSRF-Token'] = $rootScope._csrf; 57 | 58 | $http(req). 59 | success(function(res) { 60 | if (level === 1) 61 | if (historical) 62 | $scope.node3 = res; 63 | else 64 | $scope.node1 = res; 65 | if (level === 2) 66 | if (historical) 67 | $scope.node4 = res; 68 | else 69 | $scope.node2 = res; 70 | }); 71 | }; 72 | 73 | $scope.getCompleteTestHistory = function() { 74 | var req = { 75 | method: 'POST', 76 | url: '/getTestHistory', 77 | data: { 78 | query: { 79 | id: $scope.test.id, 80 | name: $scope.test.name 81 | } 82 | } 83 | }; 84 | 85 | $http.defaults.headers.post['X-CSRF-Token'] = $rootScope._csrf; 86 | 87 | $http(req). 88 | success(function(res) { 89 | $scope.historicalTestList = res; 90 | }); 91 | }; 92 | 93 | $scope.getMostRecentTestByName = function(name) { 94 | var req = { 95 | method: 'POST', 96 | url: '/getMostRecentTestByName', 97 | data: { 98 | query: { 99 | name: name 100 | } 101 | } 102 | }; 103 | 104 | $http.defaults.headers.post['X-CSRF-Token'] = $rootScope._csrf; 105 | 106 | $http(req). 107 | success(function(res) { 108 | $scope.test = res; 109 | }); 110 | }; 111 | 112 | $scope.loadHistoryById = function(testId) { 113 | $scope.loadTestById(testId, true); 114 | }; 115 | 116 | if ($location.path() === $scope.path && window.location.href.indexOf('?') > 0) { 117 | if (window.location.href.split('?')[1].indexOf('id=') === 0) { 118 | var id = window.location.href.split('?')[1].replace('id=', ''); 119 | $scope.loadTestById(id); 120 | } 121 | 122 | if (window.location.href.split('?')[1].indexOf('name=') === 0) { 123 | var name = window.location.href.split('?')[1].replace('name=', ''); 124 | $scope.getMostRecentTestByName(name); 125 | } 126 | } 127 | }]); -------------------------------------------------------------------------------- /assets/js/api/controllers/UserController.js: -------------------------------------------------------------------------------- 1 | angular.module("ExtentX") 2 | .controller("UserController", ["$scope", "$rootScope", "$http", "$window", '$uibModal', '$timeout', function($scope, $rootScope, $http, $window, $uibModal, $timeout) { 3 | $scope.isSignedOn = function() { 4 | $rootScope.signedOn = false; 5 | $rootScope.user = null; 6 | $rootScope.isAdmin = false; 7 | $scope.changeSuccess = false; 8 | 9 | setTimeout(function() { 10 | $http({ 11 | method: 'GET', 12 | url: '/isSignedOn' 13 | }). 14 | success(function(response) { 15 | if (response && response.user) { 16 | $rootScope.signedOn = true; 17 | $rootScope.user = response.user.name; 18 | $rootScope.isAdmin = response.user.admin; 19 | } 20 | }). 21 | error(function(response) { 22 | if (typeof response !== 'undefined' && response != null) 23 | console.log(response); 24 | }); 25 | }, 50); 26 | }; 27 | 28 | $scope.signon = function(query) { 29 | var req = { 30 | method: 'POST', 31 | url: '/signon', 32 | data: { 33 | query: query 34 | } 35 | }; 36 | 37 | $http.defaults.headers.post['X-CSRF-Token'] = $rootScope._csrf; 38 | 39 | $http(req). 40 | success(function(response) { 41 | $rootScope.signedOn = true; 42 | $rootScope.user = response.user.name; 43 | $rootScope.isAdmin = response.user.admin; 44 | 45 | $scope.query = null; 46 | 47 | var closeModal = function() { 48 | $rootScope.modal.close(); 49 | }; 50 | $timeout(closeModal, 1000); 51 | }). 52 | error(function(response) { 53 | $scope.signonSuccess = false; 54 | }); 55 | }; 56 | 57 | $scope.logout = function() { 58 | $http({ 59 | method: 'GET', 60 | url: '/logout' 61 | }).then(function(response) { 62 | $rootScope.signedOn = false; 63 | $rootScope.user = null; 64 | $rootScope.isAdmin = false; 65 | 66 | $window.location.reload(); 67 | }); 68 | }; 69 | 70 | $scope.animationsEnabled = true; 71 | $scope.open = function (size) { 72 | var modalInstance = $uibModal.open({ 73 | animation: $scope.animationsEnabled, 74 | templateUrl: 'signon.html', 75 | controller: 'ModalInstanceController', 76 | size: size, 77 | resolve: { 78 | items: function () { 79 | return $scope.items; 80 | } 81 | } 82 | }); 83 | 84 | $rootScope.modal = modalInstance; 85 | }; 86 | 87 | $scope.changePassword = function(query) { 88 | query.user = $scope.user; 89 | 90 | var req = { 91 | method: 'POST', 92 | url: '/changePassword', 93 | data: { 94 | query: query 95 | } 96 | }; 97 | 98 | $scope.query = null; 99 | 100 | $http.defaults.headers.post['X-CSRF-Token'] = $rootScope._csrf; 101 | 102 | $http(req). 103 | success(function(response) { 104 | $scope.changeSuccess = true; 105 | $scope.changeError = null; 106 | }). 107 | error(function(response) { 108 | $scope.changeError = response.message; 109 | }); 110 | }; 111 | 112 | }]); -------------------------------------------------------------------------------- /assets/js/api/directives/analysisViewConfig.js: -------------------------------------------------------------------------------- 1 | angular.module('ExtentX'). 2 | directive('analysisViewConfig', ['$rootScope', function($rootScope) { 3 | return { 4 | restrict: 'A', 5 | template: '
', 6 | link: function($scope, element, attrs) { 7 | $scope.setNodesPanelHeight = function() { 8 | setTimeout(function() { 9 | //var rect = document.getElementById("report-list-summary").getBoundingClientRect(); 10 | //angular.element("#notes").css("height", rect.bottom - 75); 11 | }, 50); 12 | }; 13 | } 14 | } 15 | }]); -------------------------------------------------------------------------------- /assets/js/api/directives/reportCategoryView.js: -------------------------------------------------------------------------------- 1 | angular.module('ExtentX'). 2 | directive('reportCategoryView', ['$rootScope', function($rootScope) { 3 | var usedWidth = 890; 4 | 5 | setDetailsViewContrainerWidths(); 6 | 7 | $(window).resize(function() { 8 | setDetailsViewContrainerWidths(); 9 | }); 10 | 11 | function setDetailsViewContrainerWidths() { 12 | if (!$('.category-report-view') || $('.category-report-view').length === 0) return; 13 | 14 | var freeWidth = $(document).width() - usedWidth; 15 | 16 | $('.category-report-view .category-test-panel').css('width', freeWidth); 17 | angular.element(".navbar").addClass("hidden"); 18 | } 19 | 20 | $('body').click(function(evt) { 21 | var t = $(evt.target); 22 | 23 | if (t.is('.report-name')) { 24 | $('.report-name').removeClass('active'); 25 | t.addClass('active'); 26 | } 27 | }) 28 | 29 | return { 30 | restrict: 'A', 31 | template: '', 32 | link: function($scope, element, attrs) { 33 | $scope.setContrainerWidths = function() { 34 | setTimeout(function() { 35 | setDetailsViewContrainerWidths(); 36 | }, 50); 37 | } 38 | } 39 | } 40 | }]); -------------------------------------------------------------------------------- /assets/js/api/directives/reportDetailsView.js: -------------------------------------------------------------------------------- 1 | angular.module('ExtentX'). 2 | directive('reportDetailsView', ['$rootScope', function($rootScope) { 3 | var navWidth, testNamesColWidth, usedWidth; 4 | 5 | function init() { 6 | navWidth = parseInt($('#side-nav').css('width').replace('px', '')); 7 | testNamesColWidth = parseInt($('.report-view .test-name-panel').css('width').replace('px', '')); 8 | usedWidth = navWidth + testNamesColWidth; 9 | } 10 | 11 | setDetailsViewContrainerWidths(); 12 | 13 | $(window).resize(function() { 14 | setDetailsViewContrainerWidths(); 15 | }); 16 | 17 | function setDetailsViewContrainerWidths() { 18 | init(); 19 | 20 | if (!$('.report-view') || $('.report-view').length === 0) return; 21 | 22 | var freeWidth = $(document).width() - usedWidth; 23 | 24 | $('.report-view .test-panel, .report-view .history-panel').css('width', (freeWidth / 2)); 25 | $('.report-view .history-panel').css('left', usedWidth + (freeWidth / 2) - 5); 26 | angular.element(".navbar").addClass("hidden"); 27 | } 28 | 29 | $('body').click(function(evt) { 30 | var t = $(evt.target); 31 | 32 | if (t.is('.test-name')) { 33 | $('.test-name').removeClass('active'); 34 | t.addClass('active'); 35 | } 36 | }) 37 | 38 | return { 39 | restrict: 'E', 40 | template: '', 41 | link: function($scope, element, attrs) { 42 | $scope.setContrainerWidths = function() { 43 | setTimeout(function() { 44 | setDetailsViewContrainerWidths(); 45 | }, 50); 46 | } 47 | } 48 | } 49 | }]); -------------------------------------------------------------------------------- /assets/js/api/directives/reportExceptionView.js: -------------------------------------------------------------------------------- 1 | angular.module('ExtentX'). 2 | directive('reportExceptionView', ['$rootScope', function($rootScope) { 3 | var usedWidth = 890; 4 | 5 | setDetailsViewContrainerWidths(); 6 | 7 | $(window).resize(function() { 8 | setDetailsViewContrainerWidths(); 9 | }); 10 | 11 | function setDetailsViewContrainerWidths() { 12 | if (!$('.exception-report-view') || $('.exception-report-view').length === 0) return; 13 | 14 | var freeWidth = $(document).width() - usedWidth; 15 | 16 | $('.exception-report-view .exception-test-panel').css('width', freeWidth); 17 | angular.element(".navbar").addClass("hidden"); 18 | } 19 | 20 | $('body').click(function(evt) { 21 | var t = $(evt.target); 22 | 23 | if (t.is('.report-name')) { 24 | $('.report-name').removeClass('active'); 25 | t.addClass('active'); 26 | } 27 | }) 28 | 29 | return { 30 | restrict: 'A', 31 | template: '', 32 | link: function($scope, element, attrs) { 33 | $scope.setContrainerWidths = function() { 34 | console.log('in') 35 | setTimeout(function() { 36 | setDetailsViewContrainerWidths(); 37 | }, 50); 38 | } 39 | } 40 | } 41 | }]); -------------------------------------------------------------------------------- /assets/js/api/services/BarChartSettings.js: -------------------------------------------------------------------------------- 1 | angular.module('ExtentX'). 2 | factory('BarChartSettings', function() { 3 | var options = { 4 | responsive: false, 5 | maintainAspectRatio: true, 6 | scales: { 7 | yAxes: [{ 8 | gridLines: { 9 | display: true, 10 | lineWidth: 1, 11 | }, 12 | ticks: { 13 | beginAtZero: true, 14 | mirror: false, 15 | suggestedMin: 0, 16 | }, 17 | stacked: true, 18 | }], 19 | xAxes: [{ 20 | gridLines: { 21 | display: false 22 | }, 23 | ticks: { 24 | fontSize: 10 25 | }, 26 | barPercentage: 0.1, 27 | }], 28 | } 29 | }; 30 | return { 31 | options: options 32 | } 33 | }); -------------------------------------------------------------------------------- /assets/js/api/services/CSRFToken.js: -------------------------------------------------------------------------------- 1 | angular.module('ExtentX'). 2 | run(['$http', '$rootScope', function($http, $rootScope) { 3 | $http.get('/csrfToken'). 4 | success(function(res) { 5 | $rootScope._csrf = res._csrf; 6 | }). 7 | error(function(res) { 8 | console.log(res); 9 | }); 10 | }]); -------------------------------------------------------------------------------- /assets/js/api/services/ChartSettings.js: -------------------------------------------------------------------------------- 1 | angular.module('ExtentX'). 2 | factory('ChartSettings', function() { 3 | var scales = { 4 | xAxes: [{ 5 | gridLines: { 6 | color: "rgba(242, 242, 242, .7)", 7 | }, 8 | ticks: { 9 | fontSize: 11, 10 | } 11 | }], 12 | yAxes: [{ 13 | gridLines: { 14 | color: "rgba(242, 242, 242, .7)", 15 | }, 16 | ticks: { 17 | fontSize: 11, 18 | userCallback: function(label, index, labels) { 19 | if (Math.floor(label) === label) { 20 | return label; 21 | } 22 | }, 23 | }, 24 | beginAtZero: true, 25 | }] 26 | }; 27 | 28 | return { 29 | scales: scales, 30 | } 31 | }); 32 | 33 | -------------------------------------------------------------------------------- /assets/js/api/services/DataPointFormat.js: -------------------------------------------------------------------------------- 1 | angular.module('ExtentX'). 2 | factory('DataPointFormat', ['$rootScope', function($rootScope) { 3 | return { 4 | getDataPointFormat: function(report, ix) { 5 | switch($rootScope.trendDataPointFormat) { 6 | case 'num': 7 | return ix + 1; 8 | case 'dt': 9 | var date = new Date(report.startTime); 10 | return (date.getMonth() + 1) + '/' + date.getDate() + '/' + date.getFullYear(); 11 | case 'name': 12 | return report.name; 13 | default: 14 | return (new Date(report.startTime)).toLocaleString().split(","); 15 | } 16 | } 17 | } 18 | }]); -------------------------------------------------------------------------------- /assets/js/api/services/Icon.js: -------------------------------------------------------------------------------- 1 | angular.module('ExtentX'). 2 | factory('Icon', function() { 3 | return { 4 | getIcon: function(status) { 5 | switch (status) { 6 | case 'pass': return('check_circle'); 7 | case 'fail': return('cancel'); 8 | case 'fatal': return('cancel'); 9 | case 'error': return('error'); 10 | case 'warning': return('warning'); 11 | case 'skip': return('redo'); 12 | case 'info': return('info_outline'); 13 | default: 14 | return('fa fa-question'); 15 | }; 16 | } 17 | } 18 | }); -------------------------------------------------------------------------------- /assets/js/api/services/LineChartSettings.js: -------------------------------------------------------------------------------- 1 | angular.module('ExtentX'). 2 | factory('LineChartSettings', function() { 3 | var options = { 4 | scaleFontSize: 10, 5 | scales: { 6 | yAxes: [{ 7 | display: true, 8 | ticks: { 9 | beginAtZero: true 10 | } 11 | }] 12 | } 13 | }; 14 | 15 | var datasetOverrideGreen = [ 16 | { 17 | borderWidth: 1, 18 | backgroundColor: 'rgba(75,192,192,0.05)', 19 | borderColor: 'rgba(75,192,192,1)', 20 | pointBackgroundColor: "#00AF9A", 21 | pointHoverBackgroundColor: 'rgba(75,192,192,0.1)', 22 | pointHoverBorderColor: "rgba(75,192,192,0.1)", 23 | type: 'line' 24 | }, 25 | ]; 26 | 27 | var datasetOverrideRed = [ 28 | { 29 | borderWidth: 1, 30 | backgroundColor: 'rgba(255,90,94,.2)', 31 | borderColor: 'rgba(255,90,94,.5)', 32 | pointBackgroundColor: '#FF6347', 33 | pointHoverBackgroundColor: 'rgba(255,90,94,.2)', 34 | pointHoverBorderColor: "rgba(255,90,94,.2)", 35 | type: 'line' 36 | } 37 | ]; 38 | 39 | var datasetOverride = [ 40 | { 41 | label: "Pass", 42 | borderWidth: 1, 43 | backgroundColor: 'rgba(75,192,192,0.1)', 44 | borderColor: '#33cc99', 45 | pointBackgroundColor: "#00AF9A", 46 | pointHoverBackgroundColor: 'rgba(75,192,192,0.1)', 47 | pointHoverBorderColor: "rgba(75,192,192,0.1)", 48 | type: 'line' 49 | }, 50 | { 51 | label: "Fail", 52 | borderWidth: 1, 53 | backgroundColor: 'rgba(255,90,94,.2)', 54 | borderColor: 'rgba(255,90,94,.5)', 55 | pointBackgroundColor: '#FF6347', 56 | pointHoverBackgroundColor: 'rgba(255,90,94,.2)', 57 | pointHoverBorderColor: "rgba(255,90,94,.2)", 58 | type: 'line' 59 | }, 60 | { 61 | label: "Others", 62 | borderWidth: 1, 63 | backgroundColor: 'rgba(253, 180, 92, .2)', 64 | borderColor: 'rgba(253, 180, 92, .3)', 65 | pointBackgroundColor: '#DAA520', 66 | pointHoverBackgroundColor: 'rgba(253, 180, 92, .2)', 67 | pointHoverBorderColor: "rgba(253, 180, 92, .2)", 68 | type: 'line' 69 | } 70 | ]; 71 | 72 | return { 73 | options: options, 74 | datasetOverride: datasetOverride, 75 | datasetOverrideGreen: datasetOverrideGreen, 76 | datasetOverrideRed: datasetOverrideRed, 77 | }; 78 | }); -------------------------------------------------------------------------------- /assets/js/api/services/PieChartSettings.js: -------------------------------------------------------------------------------- 1 | angular.module('ExtentX'). 2 | factory('PieChartSettings', function() { 3 | var options = { 4 | legend: { 5 | display: true, 6 | labels: { 7 | boxWidth: 10 8 | } 9 | }, 10 | responsive: false, 11 | maintainAspectRatio: true 12 | }; 13 | var colors = [ '#00af00', '#F7464A', '#8b0000', '#ff6347', '#FDB45C', '#1e90ff', '#222' ]; 14 | 15 | return { 16 | options: options, 17 | colors: colors 18 | } 19 | }); -------------------------------------------------------------------------------- /assets/js/api/services/ViewNameSetter.js: -------------------------------------------------------------------------------- 1 | angular.module('ExtentX'). 2 | factory('ViewNameSetter', ['$rootScope', function($rootScope) { 3 | return { 4 | setViewName: function setViewName(viewName) { 5 | $rootScope.viewName = viewName; 6 | } 7 | } 8 | }]); -------------------------------------------------------------------------------- /assets/js/dependencies/angular-cookies-1.5.8.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | AngularJS v1.5.8 3 | (c) 2010-2016 Google, Inc. http://angularjs.org 4 | License: MIT 5 | */ 6 | (function(n,c){'use strict';function l(b,a,g){var d=g.baseHref(),k=b[0];return function(b,e,f){var g,h;f=f||{};h=f.expires;g=c.isDefined(f.path)?f.path:d;c.isUndefined(e)&&(h="Thu, 01 Jan 1970 00:00:00 GMT",e="");c.isString(h)&&(h=new Date(h));e=encodeURIComponent(b)+"="+encodeURIComponent(e);e=e+(g?";path="+g:"")+(f.domain?";domain="+f.domain:"");e+=h?";expires="+h.toUTCString():"";e+=f.secure?";secure":"";f=e.length+1;409612 | You are not authorized to view this page. 13 |
14 |Timestamp | 15 |Status | 16 |Details | 17 |
---|---|---|
{{ log.timestamp | date: 'HH:mm:ss' }} | 22 |23 | 24 | | 25 |26 | |
Name | 14 |Report ID | 15 |Date | 16 |Status | 17 |
---|---|---|---|
{{histTest.name}} | 22 |{{histTest.report.id}} | 23 |{{histTest.startTime|date: 'MMM-dd-yyyy HH:mm:ss'}} | 24 |{{getIcon(histTest.status)}} | 25 |
<%= i18n('That\'s right-- you can use either i18n() or __()') %>
25 | ``` 26 | 27 | ## Configuration 28 | Localization/internationalization config can be found in `config/i18n.js`, from where you can set your supported locales. 29 | -------------------------------------------------------------------------------- /config/locales/de.json: -------------------------------------------------------------------------------- 1 | { 2 | "Welcome": "Willkommen", 3 | "A brand new app.": "Eine neue App." 4 | } 5 | -------------------------------------------------------------------------------- /config/locales/en.json: -------------------------------------------------------------------------------- 1 | { 2 | "Welcome": "Welcome", 3 | "A brand new app.": "A brand new app." 4 | } 5 | -------------------------------------------------------------------------------- /config/locales/es.json: -------------------------------------------------------------------------------- 1 | { 2 | "Welcome": "Bienvenido", 3 | "A brand new app.": "Una nueva aplicación." 4 | } 5 | -------------------------------------------------------------------------------- /config/locales/fr.json: -------------------------------------------------------------------------------- 1 | { 2 | "Welcome": "Bienvenue", 3 | "A brand new app.": "Une toute nouvelle application." 4 | } 5 | -------------------------------------------------------------------------------- /config/log.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Built-in Log Configuration 3 | * (sails.config.log) 4 | * 5 | * Configure the log level for your app, as well as the transport 6 | * (Underneath the covers, Sails uses Winston for logging, which 7 | * allows for some pretty neat custom transports/adapters for log messages) 8 | * 9 | * For more information on the Sails logger, check out: 10 | * http://sailsjs.org/#!/documentation/concepts/Logging 11 | */ 12 | 13 | module.exports.log = { 14 | 15 | /*************************************************************************** 16 | * * 17 | * Valid `level` configs: i.e. the minimum log level to capture with * 18 | * sails.log.*() * 19 | * * 20 | * The order of precedence for log levels from lowest to highest is: * 21 | * silly, verbose, info, debug, warn, error * 22 | * * 23 | * You may also set the level to "silent" to suppress all logs. * 24 | * * 25 | ***************************************************************************/ 26 | 27 | // level: 'info' 28 | 29 | }; 30 | -------------------------------------------------------------------------------- /config/models.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Default model configuration 3 | * (sails.config.models) 4 | * 5 | * Unless you override them, the following properties will be included 6 | * in each of your models. 7 | * 8 | * For more info on Sails models, see: 9 | * http://sailsjs.org/#!/documentation/concepts/ORM 10 | */ 11 | 12 | module.exports.models = { 13 | 14 | /*************************************************************************** 15 | * * 16 | * Your app's default connection. i.e. the name of one of your app's * 17 | * connections (see `config/connections.js`) * 18 | * * 19 | ***************************************************************************/ 20 | connection: 'extent', 21 | 22 | /*************************************************************************** 23 | * * 24 | * How and whether Sails will attempt to automatically rebuild the * 25 | * tables/collections/etc. in your schema. * 26 | * * 27 | * See http://sailsjs.org/#!/documentation/concepts/ORM/model-settings.html * 28 | * * 29 | ***************************************************************************/ 30 | migrate: 'alter' 31 | 32 | }; 33 | -------------------------------------------------------------------------------- /config/policies.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Policy Mappings 3 | * (sails.config.policies) 4 | * 5 | * Policies are simple functions which run **before** your controllers. 6 | * You can apply one or more policies to a given controller, or protect 7 | * its actions individually. 8 | * 9 | * Any policy file (e.g. `api/policies/authenticated.js`) can be accessed 10 | * below by its filename, minus the extension, (e.g. "authenticated") 11 | * 12 | * For more information on how policies work, see: 13 | * http://sailsjs.org/#!/documentation/concepts/Policies 14 | * 15 | * For more information on configuring policies, check out: 16 | * http://sailsjs.org/#!/documentation/reference/sails.config/sails.config.policies.html 17 | */ 18 | 19 | 20 | module.exports.policies = { 21 | 22 | /*************************************************************************** 23 | * * 24 | * Default policy for all controllers and actions (`true` allows public * 25 | * access) * 26 | * * 27 | ***************************************************************************/ 28 | 29 | // '*': true, 30 | 31 | /*************************************************************************** 32 | * * 33 | * Here's an example of mapping some policies to run before a controller * 34 | * and its actions * 35 | * * 36 | ***************************************************************************/ 37 | // RabbitController: { 38 | 39 | // Apply the `false` policy as the default for all of RabbitController's actions 40 | // (`false` prevents all access, which ensures that nothing bad happens to our rabbits) 41 | // '*': false, 42 | 43 | // For the action `nurture`, apply the 'isRabbitMother' policy 44 | // (this overrides `false` above) 45 | // nurture : 'isRabbitMother', 46 | 47 | // Apply the `isNiceToAnimals` AND `hasRabbitFood` policies 48 | // before letting any users feed our rabbits 49 | // feed : ['isNiceToAnimals', 'hasRabbitFood'] 50 | // } 51 | }; 52 | -------------------------------------------------------------------------------- /license: -------------------------------------------------------------------------------- 1 | ExtentX Server Community Version 2 | 3 | Copyright 2016 AventStack 4 | 5 | The BSD 3-Clause License: http://opensource.org/licenses/BSD-3-Clause 6 | 7 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 11 | 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL aventstack.com BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "extentx", 3 | "private": true, 4 | "version": "1.0.2", 5 | "description": "AventStack ExtentX Community Version - Report Server for ExtentReports", 6 | "keywords": [], 7 | "dependencies": { 8 | "bcryptjs": "2.3.0", 9 | "ejs": "2.3.4", 10 | "grunt": "1.0.1", 11 | "grunt-contrib-clean": "1.0.0", 12 | "grunt-contrib-coffee": "1.0.0", 13 | "grunt-contrib-concat": "1.0.1", 14 | "grunt-contrib-copy": "1.0.0", 15 | "grunt-contrib-cssmin": "1.0.1", 16 | "grunt-contrib-jst": "1.0.0", 17 | "grunt-contrib-less": "1.3.0", 18 | "grunt-contrib-uglify": "1.0.1", 19 | "grunt-contrib-watch": "1.0.0", 20 | "grunt-sails-linker": "0.10.1", 21 | "grunt-sync": "0.5.2", 22 | "include-all": "0.1.6", 23 | "lodash": "4.15.0", 24 | "mongodb": "2.2.33", 25 | "mv": "2.1.1", 26 | "rc": "1.0.1", 27 | "sails": "0.12.4", 28 | "sails-disk": "0.10.9", 29 | "sails-mongo": "0.12.2" 30 | }, 31 | "scripts": { 32 | "debug": "node debug app.js", 33 | "start": "node app.js" 34 | }, 35 | "main": "app.js", 36 | "repository": { 37 | "type": "git", 38 | "url": "git://github.com/anshooarora/extentx.git" 39 | }, 40 | "author": "AventStack" 41 | } 42 | -------------------------------------------------------------------------------- /tasks/README.md: -------------------------------------------------------------------------------- 1 | # About the `tasks` folder 2 | 3 | The `tasks` directory is a suite of Grunt tasks and their configurations, bundled for your convenience. The Grunt integration is mainly useful for bundling front-end assets, (like stylesheets, scripts, & markup templates) but it can also be used to run all kinds of development tasks, from browserify compilation to database migrations. 4 | 5 | If you haven't used [Grunt](http://gruntjs.com/) before, be sure to check out the [Getting Started](http://gruntjs.com/getting-started) guide, as it explains how to create a [Gruntfile](http://gruntjs.com/sample-gruntfile) as well as install and use Grunt plugins. Once you're familiar with that process, read on! 6 | 7 | 8 | ### How does this work? 9 | 10 | The asset pipeline bundled in Sails is a set of Grunt tasks configured with conventional defaults designed to make your project more consistent and productive. 11 | 12 | The entire front-end asset workflow in Sails is completely customizable-- while it provides some suggestions out of the box, Sails makes no pretense that it can anticipate all of the needs you'll encounter building the browser-based/front-end portion of your application. Who's to say you're even building an app for a browser? 13 | 14 | 15 | 16 | ### What tasks does Sails run automatically? 17 | 18 | Sails runs some of these tasks (the ones in the `tasks/register` folder) automatically when you run certain commands. 19 | 20 | ###### `sails lift` 21 | 22 | Runs the `default` task (`tasks/register/default.js`). 23 | 24 | ###### `sails lift --prod` 25 | 26 | Runs the `prod` task (`tasks/register/prod.js`). 27 | 28 | ###### `sails www` 29 | 30 | Runs the `build` task (`tasks/register/build.js`). 31 | 32 | ###### `sails www --prod` (production) 33 | 34 | Runs the `buildProd` task (`tasks/register/buildProd.js`). 35 | 36 | 37 | ### Can I customize this for SASS, Angular, client-side Jade templates, etc? 38 | 39 | You can modify, omit, or replace any of these Grunt tasks to fit your requirements. You can also add your own Grunt tasks- just add a `someTask.js` file in the `grunt/config` directory to configure the new task, then register it with the appropriate parent task(s) (see files in `grunt/register/*.js`). 40 | 41 | 42 | ### Do I have to use Grunt? 43 | 44 | Nope! To disable Grunt integration in Sails, just delete your Gruntfile or disable the Grunt hook. 45 | 46 | 47 | ### What if I'm not building a web frontend? 48 | 49 | That's ok! A core tenant of Sails is client-agnosticism-- it's especially designed for building APIs used by all sorts of clients; native Android/iOS/Cordova, serverside SDKs, etc. 50 | 51 | You can completely disable Grunt by following the instructions above. 52 | 53 | If you still want to use Grunt for other purposes, but don't want any of the default web front-end stuff, just delete your project's `assets` folder and remove the front-end oriented tasks from the `grunt/register` and `grunt/config` folders. You can also run `sails new myCoolApi --no-frontend` to omit the `assets` folder and front-end-oriented Grunt tasks for future projects. You can also replace your `sails-generate-frontend` module with alternative community generators, or create your own. This allows `sails new` to create the boilerplate for native iOS apps, Android apps, Cordova apps, SteroidsJS apps, etc. 54 | 55 | -------------------------------------------------------------------------------- /tasks/config/clean.js: -------------------------------------------------------------------------------- 1 | /** 2 | * `clean` 3 | * 4 | * --------------------------------------------------------------- 5 | * 6 | * Remove the files and folders in your Sails app's web root 7 | * (conventionally a hidden directory called `.tmp/public`). 8 | * 9 | * For usage docs see: 10 | * https://github.com/gruntjs/grunt-contrib-clean 11 | * 12 | */ 13 | module.exports = function(grunt) { 14 | 15 | grunt.config.set('clean', { 16 | dev: [ 17 | '.tmp/public/js/**', 18 | '.tmp/public/partials/**', 19 | '.tmp/public/styles/**', 20 | '.tmp/public/favicon.ico', 21 | '.tmp/public/robots.txt' 22 | ], 23 | build: ['www'] 24 | }); 25 | 26 | grunt.loadNpmTasks('grunt-contrib-clean'); 27 | }; 28 | -------------------------------------------------------------------------------- /tasks/config/coffee.js: -------------------------------------------------------------------------------- 1 | /** 2 | * `coffee` 3 | * 4 | * --------------------------------------------------------------- 5 | * 6 | * Compile CoffeeScript files located in `assets/js` into Javascript 7 | * and generate new `.js` files in `.tmp/public/js`. 8 | * 9 | * For usage docs see: 10 | * https://github.com/gruntjs/grunt-contrib-coffee 11 | * 12 | */ 13 | module.exports = function(grunt) { 14 | 15 | grunt.config.set('coffee', { 16 | dev: { 17 | options: { 18 | bare: true, 19 | sourceMap: true, 20 | sourceRoot: './' 21 | }, 22 | files: [{ 23 | expand: true, 24 | cwd: 'assets/js/', 25 | src: ['**/*.coffee'], 26 | dest: '.tmp/public/js/', 27 | ext: '.js' 28 | }] 29 | } 30 | }); 31 | 32 | grunt.loadNpmTasks('grunt-contrib-coffee'); 33 | }; 34 | -------------------------------------------------------------------------------- /tasks/config/concat.js: -------------------------------------------------------------------------------- 1 | /** 2 | * `concat` 3 | * 4 | * --------------------------------------------------------------- 5 | * 6 | * Concatenates the contents of multiple JavaScript and/or CSS files 7 | * into two new files, each located at `concat/production.js` and 8 | * `concat/production.css` respectively in `.tmp/public/concat`. 9 | * 10 | * This is used as an intermediate step to generate monolithic files 11 | * that can then be passed in to `uglify` and/or `cssmin` for minification. 12 | * 13 | * For usage docs see: 14 | * https://github.com/gruntjs/grunt-contrib-concat 15 | * 16 | */ 17 | module.exports = function(grunt) { 18 | 19 | grunt.config.set('concat', { 20 | js: { 21 | src: require('../pipeline').jsFilesToInject, 22 | dest: '.tmp/public/concat/production.js' 23 | }, 24 | css: { 25 | src: require('../pipeline').cssFilesToInject, 26 | dest: '.tmp/public/concat/production.css' 27 | } 28 | }); 29 | 30 | grunt.loadNpmTasks('grunt-contrib-concat'); 31 | }; 32 | -------------------------------------------------------------------------------- /tasks/config/copy.js: -------------------------------------------------------------------------------- 1 | /** 2 | * `copy` 3 | * 4 | * --------------------------------------------------------------- 5 | * 6 | * Copy files and/or folders from your `assets/` directory into 7 | * the web root (`.tmp/public`) so they can be served via HTTP, 8 | * and also for further pre-processing by other Grunt tasks. 9 | * 10 | * #### Normal usage (`sails lift`) 11 | * Copies all directories and files (except CoffeeScript and LESS) 12 | * from the `assets/` folder into the web root -- conventionally a 13 | * hidden directory located `.tmp/public`. 14 | * 15 | * #### Via the `build` tasklist (`sails www`) 16 | * Copies all directories and files from the .tmp/public directory into a www directory. 17 | * 18 | * For usage docs see: 19 | * https://github.com/gruntjs/grunt-contrib-copy 20 | * 21 | */ 22 | module.exports = function(grunt) { 23 | 24 | grunt.config.set('copy', { 25 | dev: { 26 | files: [{ 27 | expand: true, 28 | cwd: './assets', 29 | src: ['**/*.!(coffee|less)'], 30 | dest: '.tmp/public' 31 | }] 32 | }, 33 | build: { 34 | files: [{ 35 | expand: true, 36 | cwd: '.tmp/public', 37 | src: ['**/*'], 38 | dest: 'www' 39 | }] 40 | } 41 | }); 42 | 43 | grunt.loadNpmTasks('grunt-contrib-copy'); 44 | }; 45 | -------------------------------------------------------------------------------- /tasks/config/cssmin.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Compress CSS files. 3 | * 4 | * --------------------------------------------------------------- 5 | * 6 | * Minify the intermediate concatenated CSS stylesheet which was 7 | * prepared by the `concat` task at `.tmp/public/concat/production.css`. 8 | * 9 | * Together with the `concat` task, this is the final step that minifies 10 | * all CSS files from `assets/styles/` (and potentially your LESS importer 11 | * file from `assets/styles/importer.less`) 12 | * 13 | * For usage docs see: 14 | * https://github.com/gruntjs/grunt-contrib-cssmin 15 | * 16 | */ 17 | module.exports = function(grunt) { 18 | 19 | grunt.config.set('cssmin', { 20 | dist: { 21 | src: ['.tmp/public/concat/production.css'], 22 | dest: '.tmp/public/min/production.min.css' 23 | } 24 | }); 25 | 26 | grunt.loadNpmTasks('grunt-contrib-cssmin'); 27 | }; 28 | -------------------------------------------------------------------------------- /tasks/config/jst.js: -------------------------------------------------------------------------------- 1 | /** 2 | * `jst` 3 | * 4 | * --------------------------------------------------------------- 5 | * 6 | * Precompile HTML templates using Underscore/Lodash notation into 7 | * functions, creating a `.jst` file. This can be brought into your HTML 8 | * via a 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 |