├── .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;4096 4096 bytes)!");k.cookie=e}}c.module("ngCookies",["ng"]).provider("$cookies",[function(){var b=this.defaults={};this.$get=["$$cookieReader","$$cookieWriter",function(a,g){return{get:function(d){return a()[d]},getObject:function(d){return(d=this.get(d))?c.fromJson(d):d},getAll:function(){return a()},put:function(d,a,m){g(d,a,m?c.extend({},b,m):b)},putObject:function(d,b,a){this.put(d,c.toJson(b),a)},remove:function(a,k){g(a,void 0,k?c.extend({},b,k):b)}}}]}]);c.module("ngCookies").factory("$cookieStore", 8 | ["$cookies",function(b){return{get:function(a){return b.getObject(a)},put:function(a,c){b.putObject(a,c)},remove:function(a){b.remove(a)}}}]);l.$inject=["$document","$log","$browser"];c.module("ngCookies").provider("$$cookieWriter",function(){this.$get=l})})(window,window.angular); 9 | //# sourceMappingURL=angular-cookies.min.js.map 10 | -------------------------------------------------------------------------------- /assets/js/dependencies/angular-route-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(E,d){'use strict';function y(t,l,g){return{restrict:"ECA",terminal:!0,priority:400,transclude:"element",link:function(b,e,a,c,k){function p(){m&&(g.cancel(m),m=null);h&&(h.$destroy(),h=null);n&&(m=g.leave(n),m.then(function(){m=null}),n=null)}function B(){var a=t.current&&t.current.locals;if(d.isDefined(a&&a.$template)){var a=b.$new(),c=t.current;n=k(a,function(a){g.enter(a,null,n||e).then(function(){!d.isDefined(A)||A&&!b.$eval(A)||l()});p()});h=c.scope=a;h.$emit("$viewContentLoaded"); 7 | h.$eval(s)}else p()}var h,n,m,A=a.autoscroll,s=a.onload||"";b.$on("$routeChangeSuccess",B);B()}}}function w(d,l,g){return{restrict:"ECA",priority:-400,link:function(b,e){var a=g.current,c=a.locals;e.html(c.$template);var k=d(e.contents());if(a.controller){c.$scope=b;var p=l(a.controller,c);a.controllerAs&&(b[a.controllerAs]=p);e.data("$ngControllerController",p);e.children().data("$ngControllerController",p)}b[a.resolveAs||"$resolve"]=c;k(b)}}}var x,C,s=d.module("ngRoute",["ng"]).provider("$route", 8 | function(){function t(b,e){return d.extend(Object.create(b),e)}function l(b,d){var a=d.caseInsensitiveMatch,c={originalPath:b,regexp:b},g=c.keys=[];b=b.replace(/([().])/g,"\\$1").replace(/(\/)?:(\w+)(\*\?|[\?\*])?/g,function(b,a,d,c){b="?"===c||"*?"===c?"?":null;c="*"===c||"*?"===c?"*":null;g.push({name:d,optional:!!b});a=a||"";return""+(b?"":a)+"(?:"+(b?a:"")+(c&&"(.+?)"||"([^/]+)")+(b||"")+")"+(b||"")}).replace(/([\/$\*])/g,"\\$1");c.regexp=new RegExp("^"+b+"$",a?"i":"");return c}x=d.isArray;C= 9 | d.isObject;var g={};this.when=function(b,e){var a;a=void 0;if(x(e)){a=a||[];for(var c=0,k=e.length;c 2 |
3 |
4 |   5 |
6 |

7 | Not Authorized! 8 | warning 9 |

10 | 11 |

12 | You are not authorized to view this page. 13 |

14 |
15 |
16 |
17 | 18 | 19 |
20 | 21 | 22 |
23 |
24 |
25 |
26 | Report Settings 27 |
28 | 29 |
30 |   31 |
32 |
33 | Delete reports older than     days 34 | 35 |
36 |
37 |
38 | Delete projects with all dependencies   39 | 40 | 44 | 45 |
Project and dependencies cleared!
46 |
47 |
48 |
49 | Reset database to initial setting 50 | 51 | 52 |
Reset complete!
53 |
54 |
55 |
56 |
57 |
58 | 59 |
60 | 63 |
64 |
    65 |
  1. 66 | {{obj}} deleted! 67 |
  2. 68 |
69 |
70 |
71 |
72 |
73 | 74 | 75 | 76 | 77 |
78 |
-------------------------------------------------------------------------------- /assets/partials/author-summary.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 | 5 |
6 |
7 |
8 | Author vs Test Distibution 9 |
10 |
11 | 12 |
13 |
14 |
15 | 16 | 17 |
18 |
19 |
20 | Author vs Failed Test Distibution 21 |
22 |
23 | 24 |
25 |
26 |
27 | 28 |
29 | 30 |
-------------------------------------------------------------------------------- /assets/partials/category-summary.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 | 5 |
6 |
7 |
8 | Category vs Test Distibution 9 |
10 |
11 | 12 |
13 |
14 |
15 | 16 | 17 |
18 |
19 |
20 | Category vs Failed Test Distibution 21 |
22 |
23 | 24 |
25 |
26 |
27 |
28 |
-------------------------------------------------------------------------------- /assets/partials/change-password.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | 11 | 12 |
13 |
14 | 15 | 16 |
17 |
18 | 19 | 20 |
21 |
22 | 25 |
26 |
27 | 28 | 29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 | 37 |
-------------------------------------------------------------------------------- /assets/partials/exception-summary.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 |
5 |
6 |
7 | Kudos! 8 | No Exceptions were found in your reports. 9 |
10 |
11 |
12 | 13 |
14 | 15 | 16 |
17 |
18 |
19 | Exception Distibution By Report 20 |
21 |
22 | 23 |
24 |
25 |
26 | 27 | 28 |
29 |
30 |
31 | Exception Distibution 32 |
33 |
34 | 35 |
36 |
37 |
38 |
39 | 40 |
-------------------------------------------------------------------------------- /assets/partials/index.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |

Dashboard

5 |
6 |
7 | 8 |
9 |
10 | No tests. 11 |
12 |
13 |
-------------------------------------------------------------------------------- /assets/partials/node-template.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | {{node.name}} 5 |
6 | 7 |
8 |
9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 25 | 26 | 27 | 28 |
TimestampStatusDetails
{{ log.timestamp | date: 'HH:mm:ss' }} 23 | 24 |
29 | 30 |
31 | 32 | 33 | 34 |
 
35 |
36 |
37 | 38 |
39 |
-------------------------------------------------------------------------------- /assets/partials/report-template-blocks.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 | {{$index+1}}. {{report.project.name}} 6 |
7 | 10 |
11 | {{ report.startTime | date: 'MMM-dd-yyyy HH:mm:ss' }} 12 | {{ report.endTime | date: 'MMM-dd-yyyy HH:mm:ss' }} 13 | {{report.duration}} ms 14 |
15 |
16 |
17 |
18 | 19 | -------------------------------------------------------------------------------- /assets/partials/report.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 5 |
6 | 7 | 8 |
9 |
10 |

{{report.name}}

11 | {{report.startTime | date: 'MMM dd, yyyy HH:mm:ss'}} 12 | {{report.endTime | date: 'MMM dd, yyyy HH:mm:ss'}} 13 |
14 |
15 |
    16 |
  • 17 |
    {{test.name}}
    18 | {{test.startTime | date: 'MMM dd, yyyy HH:mm:ss'}} 19 | {{getIcon(test.status)}} 20 |
  • 21 |
22 |
23 |
24 | 25 | 26 |
27 |
28 |
29 |

{{test.name}}

30 | 31 | {{getIcon(test.status)}} 32 | 33 | {{test.startTime | date: 'MMM dd, yyyy HH:mm:ss'}} 34 | {{test.endTime | date: 'MMM dd, yyyy HH:mm:ss'}} 35 | {{test.duration}} ms 36 |
37 | 38 |
39 | 40 | 41 |
42 |
43 |
44 | 45 | 46 |
47 |
48 |
49 |

{{historicalTest.name}}

50 | 51 | {{getIcon(historicalTest.status)}} 52 | 53 | {{historicalTest.startTime | date: 'MMM dd, yyyy HH:mm:ss'}} 54 | {{historicalTest.endTime | date: 'MMM dd, yyyy HH:mm:ss'}} 55 | {{historicalTest.duration}} ms 56 |
57 | 58 |
59 | {{test=historicalTest;""}} 60 | 61 |
62 |
63 |
64 | 65 |
66 | 67 |
-------------------------------------------------------------------------------- /assets/partials/test-history-template.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 6 | 7 |
8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 |
NameReport IDDateStatus
{{histTest.name}}{{histTest.report.id}}{{histTest.startTime|date: 'MMM-dd-yyyy HH:mm:ss'}}{{getIcon(histTest.status)}}
28 | 29 |
30 | No history exists for this test. 31 |
-------------------------------------------------------------------------------- /assets/partials/test.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |

{{test.name}}

5 |
6 |
7 | 8 |
9 | 10 |
11 |
12 |
13 | {{test.startTime | date: 'MMM dd, yyyy HH:mm:ss'}} 14 | {{test.endTime | date: 'MMM dd, yyyy HH:mm:ss'}} 15 | {{test.duration}} ms 16 | 17 | {{getIcon(test.status)}} 18 | 19 |
20 | 21 |
22 | 23 | 24 |
25 |
26 |
27 | 28 |
29 |
30 |
31 | {{historicalTest.startTime | date: 'MMM dd, yyyy HH:mm:ss'}} 32 | {{historicalTest.endTime | date: 'MMM dd, yyyy HH:mm:ss'}} 33 | {{historicalTest.duration}} ms 34 | 35 | {{getIcon(historicalTest.status)}} 36 | 37 |
38 | 39 |
40 | 41 |
42 |
43 |
44 | 45 |
46 | 47 | 48 |
-------------------------------------------------------------------------------- /assets/robots.txt: -------------------------------------------------------------------------------- 1 | # The robots.txt file is used to control how search engines index your live URLs. 2 | # See http://sailsjs.org/documentation/anatomy/my-app/assets/robots-txt for more information. 3 | 4 | 5 | 6 | # To prevent search engines from seeing the site altogether, uncomment the next two lines: 7 | # User-Agent: * 8 | # Disallow: / 9 | -------------------------------------------------------------------------------- /assets/styles/dependencies/featherlight-1.5.0.min.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Featherlight - ultra slim jQuery lightbox 3 | * Version 1.5.0 - http://noelboss.github.io/featherlight/ 4 | * 5 | * Copyright 2016, Noël Raoul Bossart (http://www.noelboss.com) 6 | * MIT Licensed. 7 | **/ 8 | @media all{.featherlight{display:none;position:fixed;top:0;right:0;bottom:0;left:0;z-index:2147483647;text-align:center;white-space:nowrap;cursor:pointer;background:#333;background:rgba(0,0,0,0)}.featherlight:last-of-type{background:rgba(0,0,0,.8)}.featherlight:before{content:'';display:inline-block;height:100%;vertical-align:middle;margin-right:-.25em}.featherlight .featherlight-content{position:relative;text-align:left;vertical-align:middle;display:inline-block;overflow:auto;padding:25px 25px 0;border-bottom:25px solid transparent;margin-left:5%;margin-right:5%;max-height:95%;background:#fff;cursor:auto;white-space:normal}.featherlight .featherlight-inner{display:block}.featherlight .featherlight-close-icon{position:absolute;z-index:9999;top:0;right:0;line-height:25px;width:25px;cursor:pointer;text-align:center;font-family:Arial,sans-serif;background:#fff;background:rgba(255,255,255,.3);color:#000}.featherlight .featherlight-image{width:100%}.featherlight-iframe .featherlight-content{border-bottom:0;padding:0}.featherlight iframe{border:0}.featherlight *{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}}@media only screen and (max-width:1024px){.featherlight .featherlight-content{margin-left:10px;margin-right:10px;max-height:98%;padding:10px 10px 0;border-bottom:10px solid transparent}} -------------------------------------------------------------------------------- /assets/styles/dependencies/icons/MaterialIcons-Regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anshooarora/extentx/aa7b56a11b95df16c3f9343879112c92b1323eca/assets/styles/dependencies/icons/MaterialIcons-Regular.eot -------------------------------------------------------------------------------- /assets/styles/dependencies/icons/MaterialIcons-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anshooarora/extentx/aa7b56a11b95df16c3f9343879112c92b1323eca/assets/styles/dependencies/icons/MaterialIcons-Regular.ttf -------------------------------------------------------------------------------- /assets/styles/dependencies/icons/MaterialIcons-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anshooarora/extentx/aa7b56a11b95df16c3f9343879112c92b1323eca/assets/styles/dependencies/icons/MaterialIcons-Regular.woff -------------------------------------------------------------------------------- /assets/styles/dependencies/icons/MaterialIcons-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anshooarora/extentx/aa7b56a11b95df16c3f9343879112c92b1323eca/assets/styles/dependencies/icons/MaterialIcons-Regular.woff2 -------------------------------------------------------------------------------- /assets/styles/dependencies/material-icons.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'Material Icons'; 3 | font-style: normal; 4 | font-weight: 400; 5 | src: url(icons/MaterialIcons-Regular.eot); /* For IE6-8 */ 6 | src: local('icons/Material Icons'), 7 | local('icons/MaterialIcons-Regular'), 8 | url(icons/MaterialIcons-Regular.woff2) format('woff2'), 9 | url(icons/MaterialIcons-Regular.woff) format('woff'), 10 | url(icons/MaterialIcons-Regular.ttf) format('truetype'); 11 | } 12 | 13 | .material-icons { 14 | font-family: 'Material Icons'; 15 | font-weight: normal; 16 | font-style: normal; 17 | font-size: 24px; /* Preferred icon size */ 18 | display: inline-block; 19 | line-height: 1; 20 | text-transform: none; 21 | letter-spacing: normal; 22 | word-wrap: normal; 23 | white-space: nowrap; 24 | direction: ltr; 25 | 26 | /* Support for all WebKit browsers. */ 27 | -webkit-font-smoothing: antialiased; 28 | /* Support for Safari and Chrome. */ 29 | text-rendering: optimizeLegibility; 30 | 31 | /* Support for Firefox. */ 32 | -moz-osx-font-smoothing: grayscale; 33 | 34 | /* Support for IE. */ 35 | font-feature-settings: 'liga'; 36 | } 37 | -------------------------------------------------------------------------------- /assets/templates/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anshooarora/extentx/aa7b56a11b95df16c3f9343879112c92b1323eca/assets/templates/.gitkeep -------------------------------------------------------------------------------- /config/bootstrap.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Bootstrap 3 | * (sails.config.bootstrap) 4 | * 5 | * An asynchronous bootstrap function that runs before your Sails app gets lifted. 6 | * This gives you an opportunity to set up your data model, run jobs, or perform some special logic. 7 | * 8 | * For more information on bootstrapping your app, check out: 9 | * http://sailsjs.org/#!/documentation/reference/sails.config/sails.config.bootstrap.html 10 | */ 11 | 12 | module.exports.bootstrap = function(cb) { 13 | 14 | var settings = [ 15 | { 16 | name: 'trendDataPoints', 17 | value: 5 18 | }, { 19 | name: 'trendDataPointFormat', 20 | value: 'long-dt' 21 | }, { 22 | name: 'deleteReportsOlderThanDays', 23 | value: 60 24 | } 25 | ]; 26 | 27 | sails.on('lifted', function() { 28 | // default settings 29 | Settings.find().exec(function(err, res) { 30 | if (err) console.log(err); 31 | 32 | if (!res || !res.length) { 33 | Settings.create(settings).exec(function(err, res) { }); 34 | } else { 35 | // ensure each setting is available 36 | _(settings).forEach(function(el) { 37 | Settings.findOne({ name: el.name }).exec(function(err, result) { 38 | if (typeof result === 'undefined' || result.length === 0) { 39 | Settings.create({ 40 | name: el.name, 41 | value: el.value 42 | }).exec(function(err, created) { }); 43 | } 44 | }); 45 | }); 46 | } 47 | }); 48 | 49 | // default user [admin] 50 | User.find({ user: 'admin' }).exec(function(err, user) { 51 | if (err) throw (err); 52 | 53 | if (!user || !user.length) { 54 | var bcrypt = require('bcryptjs'); 55 | var salt = bcrypt.genSaltSync(10); 56 | var hash = bcrypt.hashSync('password', salt); 57 | 58 | User.create({ 59 | name: 'root', 60 | password: hash, 61 | admin: true 62 | }).exec(function(err, created) { }); 63 | } 64 | }); 65 | }); 66 | 67 | // It's very important to trigger this callback method when you are finished 68 | // with the bootstrap! (otherwise your server will never lift, since it's waiting on the bootstrap) 69 | cb(); 70 | }; 71 | -------------------------------------------------------------------------------- /config/cors.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Cross-Origin Resource Sharing (CORS) Settings 3 | * (sails.config.cors) 4 | * 5 | * CORS is like a more modern version of JSONP-- it allows your server/API 6 | * to successfully respond to requests from client-side JavaScript code 7 | * running on some other domain (e.g. google.com) 8 | * Unlike JSONP, it works with POST, PUT, and DELETE requests 9 | * 10 | * For more information on CORS, check out: 11 | * http://en.wikipedia.org/wiki/Cross-origin_resource_sharing 12 | * 13 | * Note that any of these settings (besides 'allRoutes') can be changed on a per-route basis 14 | * by adding a "cors" object to the route configuration: 15 | * 16 | * '/get foo': { 17 | * controller: 'foo', 18 | * action: 'bar', 19 | * cors: { 20 | * origin: 'http://foobar.com,https://owlhoot.com' 21 | * } 22 | * } 23 | * 24 | * For more information on this configuration file, see: 25 | * http://sailsjs.org/#!/documentation/reference/sails.config/sails.config.cors.html 26 | * 27 | */ 28 | 29 | module.exports.cors = { 30 | 31 | /*************************************************************************** 32 | * * 33 | * Allow CORS on all routes by default? If not, you must enable CORS on a * 34 | * per-route basis by either adding a "cors" configuration object to the * 35 | * route config, or setting "cors:true" in the route config to use the * 36 | * default settings below. * 37 | * * 38 | ***************************************************************************/ 39 | 40 | // allRoutes: false, 41 | 42 | /*************************************************************************** 43 | * * 44 | * Which domains which are allowed CORS access? This can be a * 45 | * comma-delimited list of hosts (beginning with http:// or https://) or * 46 | * "*" to allow all domains CORS access. * 47 | * * 48 | ***************************************************************************/ 49 | 50 | // origin: '*', 51 | 52 | /*************************************************************************** 53 | * * 54 | * Allow cookies to be shared for CORS requests? * 55 | * * 56 | ***************************************************************************/ 57 | 58 | // credentials: true, 59 | 60 | /*************************************************************************** 61 | * * 62 | * Which methods should be allowed for CORS requests? This is only used in * 63 | * response to preflight requests (see article linked above for more info) * 64 | * * 65 | ***************************************************************************/ 66 | 67 | // methods: 'GET, POST, PUT, DELETE, OPTIONS, HEAD', 68 | 69 | /*************************************************************************** 70 | * * 71 | * Which headers should be allowed for CORS requests? This is only used in * 72 | * response to preflight requests. * 73 | * * 74 | ***************************************************************************/ 75 | 76 | // headers: 'content-type' 77 | 78 | }; 79 | -------------------------------------------------------------------------------- /config/csrf.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Cross-Site Request Forgery Protection Settings 3 | * (sails.config.csrf) 4 | * 5 | * CSRF tokens are like a tracking chip. While a session tells the server that a user 6 | * "is who they say they are", a csrf token tells the server "you are where you say you are". 7 | * 8 | * When enabled, all non-GET requests to the Sails server must be accompanied by 9 | * a special token, identified as the '_csrf' parameter. 10 | * 11 | * This option protects your Sails app against cross-site request forgery (or CSRF) attacks. 12 | * A would-be attacker needs not only a user's session cookie, but also this timestamped, 13 | * secret CSRF token, which is refreshed/granted when the user visits a URL on your app's domain. 14 | * 15 | * This allows us to have certainty that our users' requests haven't been hijacked, 16 | * and that the requests they're making are intentional and legitimate. 17 | * 18 | * This token has a short-lived expiration timeline, and must be acquired by either: 19 | * 20 | * (a) For traditional view-driven web apps: 21 | * Fetching it from one of your views, where it may be accessed as 22 | * a local variable, e.g.: 23 | *
24 | * 25 | *
26 | * 27 | * or (b) For AJAX/Socket-heavy and/or single-page apps: 28 | * Sending a GET request to the `/csrfToken` route, where it will be returned 29 | * as JSON, e.g.: 30 | * { _csrf: 'ajg4JD(JGdajhLJALHDa' } 31 | * 32 | * 33 | * Enabling this option requires managing the token in your front-end app. 34 | * For traditional web apps, it's as easy as passing the data from a view into a form action. 35 | * In AJAX/Socket-heavy apps, just send a GET request to the /csrfToken route to get a valid token. 36 | * 37 | * For more information on CSRF, check out: 38 | * http://en.wikipedia.org/wiki/Cross-site_request_forgery 39 | * 40 | * For more information on this configuration file, including info on CSRF + CORS, see: 41 | * http://sailsjs.org/#!/documentation/reference/sails.config/sails.config.csrf.html 42 | * 43 | */ 44 | 45 | /**************************************************************************** 46 | * * 47 | * Enabled CSRF protection for your site? * 48 | * * 49 | ****************************************************************************/ 50 | 51 | module.exports.csrf = true; 52 | 53 | /**************************************************************************** 54 | * * 55 | * You may also specify more fine-grained settings for CSRF, including the * 56 | * domains which are allowed to request the CSRF token via AJAX. These * 57 | * settings override the general CORS settings in your config/cors.js file. * 58 | * * 59 | ****************************************************************************/ 60 | 61 | module.exports.csrf = { 62 | grantTokenViaAjax: true, 63 | origin: '' 64 | } 65 | -------------------------------------------------------------------------------- /config/env/development.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Development environment settings 3 | * 4 | * This file can include shared settings for a development team, 5 | * such as API keys or remote database passwords. If you're using 6 | * a version control solution for your Sails app, this file will 7 | * be committed to your repository unless you add it to your .gitignore 8 | * file. If your repository will be publicly viewable, don't add 9 | * any private information to this file! 10 | * 11 | */ 12 | 13 | module.exports = { 14 | 15 | /*************************************************************************** 16 | * Set the default database connection for models in the development * 17 | * environment (see config/connections.js and config/models.js ) * 18 | ***************************************************************************/ 19 | 20 | // models: { 21 | // connection: 'someMongodbServer' 22 | // } 23 | 24 | }; 25 | -------------------------------------------------------------------------------- /config/env/production.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Production environment settings 3 | * 4 | * This file can include shared settings for a production environment, 5 | * such as API keys or remote database passwords. If you're using 6 | * a version control solution for your Sails app, this file will 7 | * be committed to your repository unless you add it to your .gitignore 8 | * file. If your repository will be publicly viewable, don't add 9 | * any private information to this file! 10 | * 11 | */ 12 | 13 | module.exports = { 14 | 15 | /*************************************************************************** 16 | * Set the default database connection for models in the production * 17 | * environment (see config/connections.js and config/models.js ) * 18 | ***************************************************************************/ 19 | 20 | // models: { 21 | // connection: 'someMysqlServer' 22 | // }, 23 | 24 | /*************************************************************************** 25 | * Set the port in the production environment to 80 * 26 | ***************************************************************************/ 27 | 28 | // port: 80, 29 | 30 | /*************************************************************************** 31 | * Set the log level in production environment to "silent" * 32 | ***************************************************************************/ 33 | 34 | // log: { 35 | // level: "silent" 36 | // } 37 | 38 | }; 39 | -------------------------------------------------------------------------------- /config/globals.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Global Variable Configuration 3 | * (sails.config.globals) 4 | * 5 | * Configure which global variables which will be exposed 6 | * automatically by Sails. 7 | * 8 | * For more information on configuration, check out: 9 | * http://sailsjs.org/#!/documentation/reference/sails.config/sails.config.globals.html 10 | */ 11 | module.exports.globals = { 12 | 13 | /**************************************************************************** 14 | * * 15 | * Expose the lodash installed in Sails core as a global variable. If this * 16 | * is disabled, like any other node module you can always run npm install * 17 | * lodash --save, then var _ = require('lodash') at the top of any file. * 18 | * * 19 | ****************************************************************************/ 20 | 21 | // _: true, 22 | 23 | /**************************************************************************** 24 | * * 25 | * Expose the async installed in Sails core as a global variable. If this is * 26 | * disabled, like any other node module you can always run npm install async * 27 | * --save, then var async = require('async') at the top of any file. * 28 | * * 29 | ****************************************************************************/ 30 | 31 | // async: true, 32 | 33 | /**************************************************************************** 34 | * * 35 | * Expose the sails instance representing your app. If this is disabled, you * 36 | * can still get access via req._sails. * 37 | * * 38 | ****************************************************************************/ 39 | 40 | // sails: true, 41 | 42 | /**************************************************************************** 43 | * * 44 | * Expose each of your app's services as global variables (using their * 45 | * "globalId"). E.g. a service defined in api/models/NaturalLanguage.js * 46 | * would have a globalId of NaturalLanguage by default. If this is disabled, * 47 | * you can still access your services via sails.services.* * 48 | * * 49 | ****************************************************************************/ 50 | 51 | // services: true, 52 | 53 | /**************************************************************************** 54 | * * 55 | * Expose each of your app's models as global variables (using their * 56 | * "globalId"). E.g. a model defined in api/models/User.js would have a * 57 | * globalId of User by default. If this is disabled, you can still access * 58 | * your models via sails.models.*. * 59 | * * 60 | ****************************************************************************/ 61 | 62 | // models: true 63 | }; 64 | -------------------------------------------------------------------------------- /config/http.js: -------------------------------------------------------------------------------- 1 | /** 2 | * HTTP Server Settings 3 | * (sails.config.http) 4 | * 5 | * Configuration for the underlying HTTP server in Sails. 6 | * Only applies to HTTP requests (not WebSockets) 7 | * 8 | * For more information on configuration, check out: 9 | * http://sailsjs.org/#!/documentation/reference/sails.config/sails.config.http.html 10 | */ 11 | 12 | module.exports.http = { 13 | 14 | /**************************************************************************** 15 | * * 16 | * Express middleware to use for every Sails request. To add custom * 17 | * middleware to the mix, add a function to the middleware config object and * 18 | * add its key to the "order" array. The $custom key is reserved for * 19 | * backwards-compatibility with Sails v0.9.x apps that use the * 20 | * `customMiddleware` config option. * 21 | * * 22 | ****************************************************************************/ 23 | 24 | middleware: { 25 | 26 | /*************************************************************************** 27 | * * 28 | * The order in which middleware should be run for HTTP request. (the Sails * 29 | * router is invoked by the "router" middleware below.) * 30 | * * 31 | ***************************************************************************/ 32 | 33 | // order: [ 34 | // 'startRequestTimer', 35 | // 'cookieParser', 36 | // 'session', 37 | // 'myRequestLogger', 38 | // 'bodyParser', 39 | // 'handleBodyParserError', 40 | // 'compress', 41 | // 'methodOverride', 42 | // 'poweredBy', 43 | // '$custom', 44 | // 'router', 45 | // 'www', 46 | // 'favicon', 47 | // '404', 48 | // '500' 49 | // ], 50 | 51 | /**************************************************************************** 52 | * * 53 | * Example custom middleware; logs each request to the console. * 54 | * * 55 | ****************************************************************************/ 56 | 57 | // myRequestLogger: function (req, res, next) { 58 | // console.log("Requested :: ", req.method, req.url); 59 | // return next(); 60 | // } 61 | 62 | 63 | /*************************************************************************** 64 | * * 65 | * The body parser that will handle incoming multipart HTTP requests. By * 66 | * default as of v0.10, Sails uses * 67 | * [skipper](http://github.com/balderdashy/skipper). See * 68 | * http://www.senchalabs.org/connect/multipart.html for other options. * 69 | * * 70 | * Note that Sails uses an internal instance of Skipper by default; to * 71 | * override it and specify more options, make sure to "npm install skipper" * 72 | * in your project first. You can also specify a different body parser or * 73 | * a custom function with req, res and next parameters (just like any other * 74 | * middleware function). * 75 | * * 76 | ***************************************************************************/ 77 | 78 | // bodyParser: require('skipper')({strict: true}) 79 | 80 | }, 81 | 82 | /*************************************************************************** 83 | * * 84 | * The number of seconds to cache flat files on disk being served by * 85 | * Express static middleware (by default, these files are in `.tmp/public`) * 86 | * * 87 | * The HTTP static cache is only active in a 'production' environment, * 88 | * since that's the only time Express will cache flat-files. * 89 | * * 90 | ***************************************************************************/ 91 | 92 | // cache: 31557600000 93 | }; 94 | -------------------------------------------------------------------------------- /config/i18n.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Internationalization / Localization Settings 3 | * (sails.config.i18n) 4 | * 5 | * If your app will touch people from all over the world, i18n (or internationalization) 6 | * may be an important part of your international strategy. 7 | * 8 | * 9 | * For more informationom i18n in Sails, check out: 10 | * http://sailsjs.org/#!/documentation/concepts/Internationalization 11 | * 12 | * For a complete list of i18n options, see: 13 | * https://github.com/mashpie/i18n-node#list-of-configuration-options 14 | * 15 | * 16 | */ 17 | 18 | module.exports.i18n = { 19 | 20 | /*************************************************************************** 21 | * * 22 | * Which locales are supported? * 23 | * * 24 | ***************************************************************************/ 25 | 26 | // locales: ['en', 'es', 'fr', 'de'], 27 | 28 | /**************************************************************************** 29 | * * 30 | * What is the default locale for the site? Note that this setting will be * 31 | * overridden for any request that sends an "Accept-Language" header (i.e. * 32 | * most browsers), but it's still useful if you need to localize the * 33 | * response for requests made by non-browser clients (e.g. cURL). * 34 | * * 35 | ****************************************************************************/ 36 | 37 | // defaultLocale: 'en', 38 | 39 | /**************************************************************************** 40 | * * 41 | * Automatically add new keys to locale (translation) files when they are * 42 | * encountered during a request? * 43 | * * 44 | ****************************************************************************/ 45 | 46 | // updateFiles: false, 47 | 48 | /**************************************************************************** 49 | * * 50 | * Path (relative to app root) of directory to store locale (translation) * 51 | * files in. * 52 | * * 53 | ****************************************************************************/ 54 | 55 | // localesDirectory: '/config/locales' 56 | 57 | }; 58 | -------------------------------------------------------------------------------- /config/locales/_README.md: -------------------------------------------------------------------------------- 1 | # Internationalization / Localization Settings 2 | 3 | > Also see the official docs on internationalization/localization: 4 | > http://links.sailsjs.org/docs/config/locales 5 | 6 | ## Locales 7 | All locale files live under `config/locales`. Here is where you can add translations 8 | as JSON key-value pairs. The name of the file should match the language that you are supporting, which allows for automatic language detection based on request headers. 9 | 10 | Here is an example locale stringfile for the Spanish language (`config/locales/es.json`): 11 | ```json 12 | { 13 | "Hello!": "Hola!", 14 | "Hello %s, how are you today?": "¿Hola %s, como estas?", 15 | } 16 | ``` 17 | ## Usage 18 | Locales can be accessed in controllers/policies through `res.i18n()`, or in views through the `__(key)` or `i18n(key)` functions. 19 | Remember that the keys are case sensitive and require exact key matches, e.g. 20 | 21 | ```ejs 22 |

<%= __('Welcome to PencilPals!') %>

23 |

<%= i18n('Hello %s, how are you today?', 'Pencil Maven') %>

24 |

<%= 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 | 71 | 72 | -------------------------------------------------------------------------------- /views/partials/dataPointsSetting.ejs: -------------------------------------------------------------------------------- 1 |
2 | 40 |
41 | 42 | -------------------------------------------------------------------------------- /views/partials/header.ejs: -------------------------------------------------------------------------------- 1 | 47 | 48 | <% include sidenav %> 49 | <% include signon %> 50 | <% include dataPointsSetting %> 51 | -------------------------------------------------------------------------------- /views/partials/sidenav.ejs: -------------------------------------------------------------------------------- 1 |
2 | 20 |
-------------------------------------------------------------------------------- /views/partials/signon.ejs: -------------------------------------------------------------------------------- 1 |
2 | 45 |
46 | --------------------------------------------------------------------------------