diff --git a/Gruntfile.js b/Gruntfile.js
index 4c50be920fa914c5dd2605e0724abccc62e9a885..c0da0e90cf983c21adc836c401f5ecebf408651a 100644
--- a/Gruntfile.js
+++ b/Gruntfile.js
@@ -8,18 +8,24 @@ module.exports = function (grunt) {
     'use strict';
 
     var _ = require('underscore'),
-        path = require('path');
+        path = require('path'),
+        configDir = './dev/tools/grunt/configs',
+        taskDir = './dev/tools/grunt/tasks';
 
-    require('./dev/tools/grunt/tasks/mage-minify')(grunt);
-    require('time-grunt')(grunt);
+    [
+        taskDir + '/mage-minify',
+        taskDir + '/deploy',
+        'time-grunt'
+    ].forEach(function (task) {
+        require(task)(grunt);
+    });
 
     require('load-grunt-config')(grunt, {
-        configPath: path.join(process.cwd(), 'dev/tools/grunt/configs'),
+        configPath: path.join(__dirname, configDir),
         init: true,
         loadGruntTasks: {
             pattern: [
-                'grunt-*',
-                '!grunt-template-jasmine-requirejs'
+                'grunt-*'
             ]
         }
     });
@@ -78,24 +84,6 @@ module.exports = function (grunt) {
             'clean:pub'
         ],
 
-        spec: [
-            'specRunner:lib',
-            'specRunner:backend',
-            'specRunner:frontend'
-        ],
-
-        unit: [
-            'jasmine:lib-unit',
-            'jasmine:backend-unit',
-            'jasmine:frontend-unit'
-        ],
-
-        integration: [
-            'jasmine:lib-integration',
-            'jasmine:backend-integration',
-            'jasmine:frontend-integration'
-        ],
-
         'legacy-build': [
             'mage-minify:legacy'
         ],
@@ -104,7 +92,15 @@ module.exports = function (grunt) {
             'usebanner:documentationCss',
             'usebanner:documentationLess',
             'usebanner:documentationHtml'
-        ]
+        ],
+
+        spec: function (theme) {
+            var runner = require('./dev/tests/js/jasmine/spec_runner');
+
+            runner.init(grunt, { theme: theme });
+
+            grunt.task.run(runner.getTasks());
+        }
     }, function (task, name) {
         grunt.registerTask(name, task);
     });
diff --git a/dev/tests/js/.gitignore b/dev/tests/js/JsTestDriver/.gitignore
similarity index 100%
rename from dev/tests/js/.gitignore
rename to dev/tests/js/JsTestDriver/.gitignore
diff --git a/dev/tests/js/framework/qunit/qunit-1.14.0.css b/dev/tests/js/JsTestDriver/framework/qunit/qunit-1.14.0.css
similarity index 100%
rename from dev/tests/js/framework/qunit/qunit-1.14.0.css
rename to dev/tests/js/JsTestDriver/framework/qunit/qunit-1.14.0.css
diff --git a/dev/tests/js/framework/qunit/qunit-1.14.0.js b/dev/tests/js/JsTestDriver/framework/qunit/qunit-1.14.0.js
similarity index 100%
rename from dev/tests/js/framework/qunit/qunit-1.14.0.js
rename to dev/tests/js/JsTestDriver/framework/qunit/qunit-1.14.0.js
diff --git a/dev/tests/js/framework/requirejs-util.js b/dev/tests/js/JsTestDriver/framework/requirejs-util.js
similarity index 100%
rename from dev/tests/js/framework/requirejs-util.js
rename to dev/tests/js/JsTestDriver/framework/requirejs-util.js
diff --git a/dev/tests/js/framework/stub.js b/dev/tests/js/JsTestDriver/framework/stub.js
similarity index 100%
rename from dev/tests/js/framework/stub.js
rename to dev/tests/js/JsTestDriver/framework/stub.js
diff --git a/dev/tests/js/jsTestDriver.php.dist b/dev/tests/js/JsTestDriver/jsTestDriver.php.dist
similarity index 92%
rename from dev/tests/js/jsTestDriver.php.dist
rename to dev/tests/js/JsTestDriver/jsTestDriver.php.dist
index 49d93ba262e50d6a6c5916bbf82ad731d659994d..e3f8985644d0bcfc909b299627cf885f045d6099 100644
--- a/dev/tests/js/jsTestDriver.php.dist
+++ b/dev/tests/js/JsTestDriver/jsTestDriver.php.dist
@@ -11,7 +11,7 @@
 return array(
     'server' => 'http://localhost:9876',
     'load' => array(
-        '/dev/tests/js/framework',
+        '/dev/tests/js/JsTestDriver/framework',
         '/lib/web/mage/webapi.js',
         '/lib/web/mage/validation/validation.js',
         '/app/code/Magento/DesignEditor/view/adminhtml/web/js/infinitescroll.js',
@@ -33,6 +33,6 @@ return array(
         '/lib/web/mage/gallery-fullscreen.js',
         '/lib/web/mage/zoom.js',
     ),
-    'test' => array('/dev/tests/js/testsuite'),
+    'test' => array('/dev/tests/js/JsTestDriver/testsuite'),
     'JsTestDriver' => '{{path_to_jstestdriver_jar}}'
 );
diff --git a/dev/tests/js/jsTestDriverOrder.php b/dev/tests/js/JsTestDriver/jsTestDriverOrder.php
similarity index 92%
rename from dev/tests/js/jsTestDriverOrder.php
rename to dev/tests/js/JsTestDriver/jsTestDriverOrder.php
index 763e221c28cde73759221a3980d0e34758b5e303..b12aef10f57a3c4eea9993b1fa2092e7c5848e56 100644
--- a/dev/tests/js/jsTestDriverOrder.php
+++ b/dev/tests/js/JsTestDriver/jsTestDriverOrder.php
@@ -14,7 +14,7 @@ return [
     '/lib/web/underscore.js',
     '/lib/web/mage/template.js',
     '/lib/web/jquery/jquery-ui-1.9.2.js',
-    '/dev/tests/js/framework/requirejs-util.js',
+    '/dev/tests/js/JsTestDriver/framework/requirejs-util.js',
     '/lib/web/jquery/jquery.cookie.js',
     '/lib/web/mage/apply/main.js',
     '/lib/web/mage/mage.js',
diff --git a/dev/tests/js/run_js_tests.php b/dev/tests/js/JsTestDriver/run_js_tests.php
similarity index 98%
rename from dev/tests/js/run_js_tests.php
rename to dev/tests/js/JsTestDriver/run_js_tests.php
index 3bafc3318adeb11d0e8a17e33ae776c124901861..846b4681e197d1d524d1eb21a5a03add3b909b13 100644
--- a/dev/tests/js/run_js_tests.php
+++ b/dev/tests/js/JsTestDriver/run_js_tests.php
@@ -6,8 +6,8 @@
  * See COPYING.txt for license details.
  */
 
-define('RELATIVE_APP_ROOT', '../../..');
-require __DIR__ . '/../../../app/autoload.php';
+define('RELATIVE_APP_ROOT', '../../../..');
+require __DIR__ . '/../../../../app/autoload.php';
 
 $userConfig = normalize('jsTestDriver.php');
 $defaultConfig = normalize('jsTestDriver.php.dist');
diff --git a/dev/tests/js/testsuite/lib/ko/datepicker/datepicker.js b/dev/tests/js/JsTestDriver/testsuite/lib/ko/datepicker/datepicker.js
similarity index 100%
rename from dev/tests/js/testsuite/lib/ko/datepicker/datepicker.js
rename to dev/tests/js/JsTestDriver/testsuite/lib/ko/datepicker/datepicker.js
diff --git a/dev/tests/js/testsuite/lib/ko/datepicker/index.html b/dev/tests/js/JsTestDriver/testsuite/lib/ko/datepicker/index.html
similarity index 100%
rename from dev/tests/js/testsuite/lib/ko/datepicker/index.html
rename to dev/tests/js/JsTestDriver/testsuite/lib/ko/datepicker/index.html
diff --git a/dev/tests/js/testsuite/lib/storage/index.html b/dev/tests/js/JsTestDriver/testsuite/lib/storage/index.html
similarity index 100%
rename from dev/tests/js/testsuite/lib/storage/index.html
rename to dev/tests/js/JsTestDriver/testsuite/lib/storage/index.html
diff --git a/dev/tests/js/testsuite/lib/storage/test-storage.js b/dev/tests/js/JsTestDriver/testsuite/lib/storage/test-storage.js
similarity index 100%
rename from dev/tests/js/testsuite/lib/storage/test-storage.js
rename to dev/tests/js/JsTestDriver/testsuite/lib/storage/test-storage.js
diff --git a/dev/tests/js/testsuite/mage/_demo/index.html b/dev/tests/js/JsTestDriver/testsuite/mage/_demo/index.html
similarity index 100%
rename from dev/tests/js/testsuite/mage/_demo/index.html
rename to dev/tests/js/JsTestDriver/testsuite/mage/_demo/index.html
diff --git a/dev/tests/js/testsuite/mage/_demo/test.js b/dev/tests/js/JsTestDriver/testsuite/mage/_demo/test.js
similarity index 100%
rename from dev/tests/js/testsuite/mage/_demo/test.js
rename to dev/tests/js/JsTestDriver/testsuite/mage/_demo/test.js
diff --git a/dev/tests/js/testsuite/mage/accordion/accordion.js b/dev/tests/js/JsTestDriver/testsuite/mage/accordion/accordion.js
similarity index 100%
rename from dev/tests/js/testsuite/mage/accordion/accordion.js
rename to dev/tests/js/JsTestDriver/testsuite/mage/accordion/accordion.js
diff --git a/dev/tests/js/testsuite/mage/accordion/index.html b/dev/tests/js/JsTestDriver/testsuite/mage/accordion/index.html
similarity index 100%
rename from dev/tests/js/testsuite/mage/accordion/index.html
rename to dev/tests/js/JsTestDriver/testsuite/mage/accordion/index.html
diff --git a/dev/tests/js/testsuite/mage/button/button-test.js b/dev/tests/js/JsTestDriver/testsuite/mage/button/button-test.js
similarity index 100%
rename from dev/tests/js/testsuite/mage/button/button-test.js
rename to dev/tests/js/JsTestDriver/testsuite/mage/button/button-test.js
diff --git a/dev/tests/js/testsuite/mage/calendar/calendar-qunit.js b/dev/tests/js/JsTestDriver/testsuite/mage/calendar/calendar-qunit.js
similarity index 100%
rename from dev/tests/js/testsuite/mage/calendar/calendar-qunit.js
rename to dev/tests/js/JsTestDriver/testsuite/mage/calendar/calendar-qunit.js
diff --git a/dev/tests/js/testsuite/mage/calendar/calendar-test.js b/dev/tests/js/JsTestDriver/testsuite/mage/calendar/calendar-test.js
similarity index 100%
rename from dev/tests/js/testsuite/mage/calendar/calendar-test.js
rename to dev/tests/js/JsTestDriver/testsuite/mage/calendar/calendar-test.js
diff --git a/dev/tests/js/testsuite/mage/calendar/calendar.html b/dev/tests/js/JsTestDriver/testsuite/mage/calendar/calendar.html
similarity index 100%
rename from dev/tests/js/testsuite/mage/calendar/calendar.html
rename to dev/tests/js/JsTestDriver/testsuite/mage/calendar/calendar.html
diff --git a/dev/tests/js/testsuite/mage/calendar/date-range-test.js b/dev/tests/js/JsTestDriver/testsuite/mage/calendar/date-range-test.js
similarity index 100%
rename from dev/tests/js/testsuite/mage/calendar/date-range-test.js
rename to dev/tests/js/JsTestDriver/testsuite/mage/calendar/date-range-test.js
diff --git a/dev/tests/js/testsuite/mage/collapsible/content.html b/dev/tests/js/JsTestDriver/testsuite/mage/collapsible/content.html
similarity index 100%
rename from dev/tests/js/testsuite/mage/collapsible/content.html
rename to dev/tests/js/JsTestDriver/testsuite/mage/collapsible/content.html
diff --git a/dev/tests/js/testsuite/mage/collapsible/index.html b/dev/tests/js/JsTestDriver/testsuite/mage/collapsible/index.html
similarity index 100%
rename from dev/tests/js/testsuite/mage/collapsible/index.html
rename to dev/tests/js/JsTestDriver/testsuite/mage/collapsible/index.html
diff --git a/dev/tests/js/testsuite/mage/collapsible/test-collapsible.js b/dev/tests/js/JsTestDriver/testsuite/mage/collapsible/test-collapsible.js
similarity index 100%
rename from dev/tests/js/testsuite/mage/collapsible/test-collapsible.js
rename to dev/tests/js/JsTestDriver/testsuite/mage/collapsible/test-collapsible.js
diff --git a/dev/tests/js/testsuite/mage/decorate-test.js b/dev/tests/js/JsTestDriver/testsuite/mage/decorate-test.js
similarity index 100%
rename from dev/tests/js/testsuite/mage/decorate-test.js
rename to dev/tests/js/JsTestDriver/testsuite/mage/decorate-test.js
diff --git a/dev/tests/js/testsuite/mage/design_editor/adminhtml/js/infinitescroll.js b/dev/tests/js/JsTestDriver/testsuite/mage/design_editor/adminhtml/js/infinitescroll.js
similarity index 100%
rename from dev/tests/js/testsuite/mage/design_editor/adminhtml/js/infinitescroll.js
rename to dev/tests/js/JsTestDriver/testsuite/mage/design_editor/adminhtml/js/infinitescroll.js
diff --git a/dev/tests/js/testsuite/mage/dropdown/index.html b/dev/tests/js/JsTestDriver/testsuite/mage/dropdown/index.html
similarity index 100%
rename from dev/tests/js/testsuite/mage/dropdown/index.html
rename to dev/tests/js/JsTestDriver/testsuite/mage/dropdown/index.html
diff --git a/dev/tests/js/testsuite/mage/dropdown/test-dropdown.js b/dev/tests/js/JsTestDriver/testsuite/mage/dropdown/test-dropdown.js
similarity index 100%
rename from dev/tests/js/testsuite/mage/dropdown/test-dropdown.js
rename to dev/tests/js/JsTestDriver/testsuite/mage/dropdown/test-dropdown.js
diff --git a/dev/tests/js/testsuite/mage/edit_trigger/edit-trigger-test.js b/dev/tests/js/JsTestDriver/testsuite/mage/edit_trigger/edit-trigger-test.js
similarity index 100%
rename from dev/tests/js/testsuite/mage/edit_trigger/edit-trigger-test.js
rename to dev/tests/js/JsTestDriver/testsuite/mage/edit_trigger/edit-trigger-test.js
diff --git a/dev/tests/js/testsuite/mage/form/form-test.js b/dev/tests/js/JsTestDriver/testsuite/mage/form/form-test.js
similarity index 100%
rename from dev/tests/js/testsuite/mage/form/form-test.js
rename to dev/tests/js/JsTestDriver/testsuite/mage/form/form-test.js
diff --git a/dev/tests/js/testsuite/mage/gallery/gallery-fullscreen-test.js b/dev/tests/js/JsTestDriver/testsuite/mage/gallery/gallery-fullscreen-test.js
similarity index 100%
rename from dev/tests/js/testsuite/mage/gallery/gallery-fullscreen-test.js
rename to dev/tests/js/JsTestDriver/testsuite/mage/gallery/gallery-fullscreen-test.js
diff --git a/dev/tests/js/testsuite/mage/gallery/gallery-test.js b/dev/tests/js/JsTestDriver/testsuite/mage/gallery/gallery-test.js
similarity index 100%
rename from dev/tests/js/testsuite/mage/gallery/gallery-test.js
rename to dev/tests/js/JsTestDriver/testsuite/mage/gallery/gallery-test.js
diff --git a/dev/tests/js/testsuite/mage/list/index.html b/dev/tests/js/JsTestDriver/testsuite/mage/list/index.html
similarity index 100%
rename from dev/tests/js/testsuite/mage/list/index.html
rename to dev/tests/js/JsTestDriver/testsuite/mage/list/index.html
diff --git a/dev/tests/js/testsuite/mage/list/jquery-list-test.js b/dev/tests/js/JsTestDriver/testsuite/mage/list/jquery-list-test.js
similarity index 100%
rename from dev/tests/js/testsuite/mage/list/jquery-list-test.js
rename to dev/tests/js/JsTestDriver/testsuite/mage/list/jquery-list-test.js
diff --git a/dev/tests/js/testsuite/mage/loader/jquery-loader-test.js b/dev/tests/js/JsTestDriver/testsuite/mage/loader/jquery-loader-test.js
similarity index 100%
rename from dev/tests/js/testsuite/mage/loader/jquery-loader-test.js
rename to dev/tests/js/JsTestDriver/testsuite/mage/loader/jquery-loader-test.js
diff --git a/dev/tests/js/testsuite/mage/loader/loader-test.js b/dev/tests/js/JsTestDriver/testsuite/mage/loader/loader-test.js
similarity index 100%
rename from dev/tests/js/testsuite/mage/loader/loader-test.js
rename to dev/tests/js/JsTestDriver/testsuite/mage/loader/loader-test.js
diff --git a/dev/tests/js/testsuite/mage/loader/loader.html b/dev/tests/js/JsTestDriver/testsuite/mage/loader/loader.html
similarity index 100%
rename from dev/tests/js/testsuite/mage/loader/loader.html
rename to dev/tests/js/JsTestDriver/testsuite/mage/loader/loader.html
diff --git a/dev/tests/js/testsuite/mage/mage-test.js b/dev/tests/js/JsTestDriver/testsuite/mage/mage-test.js
similarity index 100%
rename from dev/tests/js/testsuite/mage/mage-test.js
rename to dev/tests/js/JsTestDriver/testsuite/mage/mage-test.js
diff --git a/dev/tests/js/testsuite/mage/menu/index.html b/dev/tests/js/JsTestDriver/testsuite/mage/menu/index.html
similarity index 100%
rename from dev/tests/js/testsuite/mage/menu/index.html
rename to dev/tests/js/JsTestDriver/testsuite/mage/menu/index.html
diff --git a/dev/tests/js/testsuite/mage/menu/test-menu.js b/dev/tests/js/JsTestDriver/testsuite/mage/menu/test-menu.js
similarity index 100%
rename from dev/tests/js/testsuite/mage/menu/test-menu.js
rename to dev/tests/js/JsTestDriver/testsuite/mage/menu/test-menu.js
diff --git a/dev/tests/js/testsuite/mage/requirejs/plugin/id-normalizer-test.js b/dev/tests/js/JsTestDriver/testsuite/mage/requirejs/plugin/id-normalizer-test.js
similarity index 100%
rename from dev/tests/js/testsuite/mage/requirejs/plugin/id-normalizer-test.js
rename to dev/tests/js/JsTestDriver/testsuite/mage/requirejs/plugin/id-normalizer-test.js
diff --git a/dev/tests/js/testsuite/mage/search/regular-search-test.js b/dev/tests/js/JsTestDriver/testsuite/mage/search/regular-search-test.js
similarity index 100%
rename from dev/tests/js/testsuite/mage/search/regular-search-test.js
rename to dev/tests/js/JsTestDriver/testsuite/mage/search/regular-search-test.js
diff --git a/dev/tests/js/testsuite/mage/suggest/suggest-test.js b/dev/tests/js/JsTestDriver/testsuite/mage/suggest/suggest-test.js
similarity index 100%
rename from dev/tests/js/testsuite/mage/suggest/suggest-test.js
rename to dev/tests/js/JsTestDriver/testsuite/mage/suggest/suggest-test.js
diff --git a/dev/tests/js/testsuite/mage/suggest/tree-suggest-test.js b/dev/tests/js/JsTestDriver/testsuite/mage/suggest/tree-suggest-test.js
similarity index 100%
rename from dev/tests/js/testsuite/mage/suggest/tree-suggest-test.js
rename to dev/tests/js/JsTestDriver/testsuite/mage/suggest/tree-suggest-test.js
diff --git a/dev/tests/js/testsuite/mage/tabs/index.html b/dev/tests/js/JsTestDriver/testsuite/mage/tabs/index.html
similarity index 100%
rename from dev/tests/js/testsuite/mage/tabs/index.html
rename to dev/tests/js/JsTestDriver/testsuite/mage/tabs/index.html
diff --git a/dev/tests/js/testsuite/mage/tabs/tabs-test.js b/dev/tests/js/JsTestDriver/testsuite/mage/tabs/tabs-test.js
similarity index 100%
rename from dev/tests/js/testsuite/mage/tabs/tabs-test.js
rename to dev/tests/js/JsTestDriver/testsuite/mage/tabs/tabs-test.js
diff --git a/dev/tests/js/testsuite/mage/tabs/tabs.js b/dev/tests/js/JsTestDriver/testsuite/mage/tabs/tabs.js
similarity index 100%
rename from dev/tests/js/testsuite/mage/tabs/tabs.js
rename to dev/tests/js/JsTestDriver/testsuite/mage/tabs/tabs.js
diff --git a/dev/tests/js/testsuite/mage/translate/translate-test.js b/dev/tests/js/JsTestDriver/testsuite/mage/translate/translate-test.js
similarity index 100%
rename from dev/tests/js/testsuite/mage/translate/translate-test.js
rename to dev/tests/js/JsTestDriver/testsuite/mage/translate/translate-test.js
diff --git a/dev/tests/js/testsuite/mage/translate_inline/translate-inline-test.js b/dev/tests/js/JsTestDriver/testsuite/mage/translate_inline/translate-inline-test.js
similarity index 100%
rename from dev/tests/js/testsuite/mage/translate_inline/translate-inline-test.js
rename to dev/tests/js/JsTestDriver/testsuite/mage/translate_inline/translate-inline-test.js
diff --git a/dev/tests/js/testsuite/mage/translate_inline_vde/translate-inline-vde-dialog-test.js b/dev/tests/js/JsTestDriver/testsuite/mage/translate_inline_vde/translate-inline-vde-dialog-test.js
similarity index 100%
rename from dev/tests/js/testsuite/mage/translate_inline_vde/translate-inline-vde-dialog-test.js
rename to dev/tests/js/JsTestDriver/testsuite/mage/translate_inline_vde/translate-inline-vde-dialog-test.js
diff --git a/dev/tests/js/testsuite/mage/translate_inline_vde/translate-inline-vde-test.js b/dev/tests/js/JsTestDriver/testsuite/mage/translate_inline_vde/translate-inline-vde-test.js
similarity index 100%
rename from dev/tests/js/testsuite/mage/translate_inline_vde/translate-inline-vde-test.js
rename to dev/tests/js/JsTestDriver/testsuite/mage/translate_inline_vde/translate-inline-vde-test.js
diff --git a/dev/tests/js/testsuite/mage/validation/index.html b/dev/tests/js/JsTestDriver/testsuite/mage/validation/index.html
similarity index 100%
rename from dev/tests/js/testsuite/mage/validation/index.html
rename to dev/tests/js/JsTestDriver/testsuite/mage/validation/index.html
diff --git a/dev/tests/js/testsuite/mage/validation/test-validation.js b/dev/tests/js/JsTestDriver/testsuite/mage/validation/test-validation.js
similarity index 100%
rename from dev/tests/js/testsuite/mage/validation/test-validation.js
rename to dev/tests/js/JsTestDriver/testsuite/mage/validation/test-validation.js
diff --git a/dev/tests/js/testsuite/mage/webapi-test.js b/dev/tests/js/JsTestDriver/testsuite/mage/webapi-test.js
similarity index 100%
rename from dev/tests/js/testsuite/mage/webapi-test.js
rename to dev/tests/js/JsTestDriver/testsuite/mage/webapi-test.js
diff --git a/dev/tests/js/testsuite/mage/zoom/zoom-test.js b/dev/tests/js/JsTestDriver/testsuite/mage/zoom/zoom-test.js
similarity index 100%
rename from dev/tests/js/testsuite/mage/zoom/zoom-test.js
rename to dev/tests/js/JsTestDriver/testsuite/mage/zoom/zoom-test.js
diff --git a/dev/tests/js/framework/spec_runner.js b/dev/tests/js/framework/spec_runner.js
deleted file mode 100644
index 733cad2f7c4da5516ad6abd9178787671a5d23e1..0000000000000000000000000000000000000000
--- a/dev/tests/js/framework/spec_runner.js
+++ /dev/null
@@ -1,200 +0,0 @@
-/**
- * Copyright © 2015 Magento. All rights reserved.
- * See COPYING.txt for license details.
- */
-/**
- * Creates jasmine configuration object
- *
- * @param  {String} type - type of tests
- * @param  {String} dir - area dir
- * @param  {Number} port - port to run on
- * @return {Object}
- */
-function buildConfig(type, dir, port) {
-    'use strict';
-
-    var isLib           = dir === 'lib',
-        requireConfigs  = [
-            '<%= path.spec %>/require.config.js',
-            '<%= path.spec %>/' + type + '/config/global.js'
-        ],
-        specsRoot       = '<%= path.spec %>/' + type,
-        specs           =  specsRoot + (isLib ? '/lib/**/*.js' : '/**/' + dir + '/**/*.js');
-    
-    if (!isLib) {
-        requireConfigs.push('<%= path.spec %>/' + type + '/config/' + dir + '.js');
-    }
-
-    return {
-        src: '<%= path.spec %>/shim.js',
-        options: {
-            host: 'http://localhost:' + port,
-            specs: specs,
-            templateOptions: {
-                requireConfigFile: requireConfigs
-            }
-        }
-    };
-}
-
-module.exports = function (grunt) {
-    'use strict';
-
-    var connect     = require('connect'),
-        logger      = require('morgan'),
-        serveStatic = require('serve-static'),
-        fs          = require('fs'),
-        root;
-
-    root = __dirname
-        .replace('/dev/tests/js/framework', '')
-        .replace('\\dev\\tests\\js\\framework', '');
-
-    grunt.registerMultiTask('specRunner', function () {
-        var app = connect(),
-            options,
-            area,
-            theme,
-            share,
-            middlewares;
-
-        options = this.options({
-            port: 3000,
-            theme: null,
-            areaDir: null,
-            shareDir: null,
-            enableLogs: false,
-            middleware: null
-        });
-
-        area    = options.areaDir;
-        share   = options.shareDir;
-        theme   = options.theme;
-
-        if (options.enableLogs) {
-            app.use(logger('dev'));
-        }
-
-        app.use(function (req, res, next) {
-            var url     = req.url,
-                match   = url.match(/^\/([A-Z][^\/]+)_(\w+)\/(.+)$/),
-                vendor,
-                module,
-                path,
-                getModuleUrl,
-                getThemeUrl;
-
-            /**
-             * Returns path to theme root folder
-             *
-             * @return {String}
-             */
-            function themeRoot() {
-                return [
-                    '/app/design',
-                    area,
-                    vendor,
-                    theme
-                ].join('/');
-            }
-
-            /**
-             * Based on 'thematic' parameter, returnes either path to theme's lib,
-             *     or 'lib/web'.
-             *
-             * @param  {Boolean} thematic
-             * @return {String}
-             */
-            function lib(thematic) {
-                return thematic ? themeRoot() + '/web' : '/lib/web';
-            }
-
-            if (match !== null) {
-                vendor  = match[1];
-                module  = match[2];
-                path    = match[3];
-
-                /**
-                 * Assembles modular path. If 'shared' flag provided and is truthy,
-                 *     will use share dir instead of area one.
-                 *
-                 * @param  {Boolean} shared
-                 * @return {String}
-                 */
-                getModuleUrl = function (shared) {
-                    return [
-                        '/app/code',
-                        vendor,
-                        module,
-                        'view',
-                        !!shared ? share : area,
-                        'web',
-                        path
-                    ].join('/');
-                };
-
-                /**
-                 * Assembles theme modular path.
-                 *
-                 * @return {String}
-                 */
-                getThemeUrl = function () {
-                    return [
-                        themeRoot(),
-                        vendor + '_' + module,
-                        'web',
-                        path
-                    ].join('/');
-                };
-
-                url = exists(url = getThemeUrl()) ?
-                    url :
-                    exists(url = getModuleUrl()) ?
-                        url : getModuleUrl(true);
-
-            } else if (canModify(url)) {
-                url = (exists(url = lib(true)) ? url : lib()) + req.url;
-            }
-
-            req.url = url;
-
-            next();
-        });
-
-        if (options.middleware && typeof options.middleware === 'function') {
-            middlewares = options.middleware(connect, options);
-
-            if (Array.isArray(middlewares)) {
-                middlewares.forEach(function (middleware) {
-                    app.use(middleware);
-                });
-            }
-        }
-
-        app.use(serveStatic(root));
-
-        app.listen(options.port);
-    });
-
-    /**
-     * Defines if passed file path exists
-     *
-     * @param  {String} path
-     * @return {Boolean}
-     */
-    function exists(path) {
-        return fs.existsSync(root + path);
-    }
-
-    /**
-     * Restricts url's which lead to '/_SpecRunner.html', '/dev/tests' or '.grunt' folders from being modified
-     *
-     * @param  {String} url
-     * @return {Boolean}
-     */
-    function canModify(url) {
-        return url.match(/^\/(\.grunt)|(dev\/tests)|(dev\\tests)|(_SpecRunner\.html)/) === null;
-    }
-
-    return { configure: buildConfig };
-};
diff --git a/dev/tests/js/spec/assets/apply/components/fn.js b/dev/tests/js/jasmine/assets/apply/components/fn.js
similarity index 100%
rename from dev/tests/js/spec/assets/apply/components/fn.js
rename to dev/tests/js/jasmine/assets/apply/components/fn.js
diff --git a/dev/tests/js/spec/assets/apply/config.json b/dev/tests/js/jasmine/assets/apply/config.json
similarity index 100%
rename from dev/tests/js/spec/assets/apply/config.json
rename to dev/tests/js/jasmine/assets/apply/config.json
diff --git a/dev/tests/js/spec/assets/apply/index.js b/dev/tests/js/jasmine/assets/apply/index.js
similarity index 94%
rename from dev/tests/js/spec/assets/apply/index.js
rename to dev/tests/js/jasmine/assets/apply/index.js
index 31268a6aa773230559203076bff7998a1d91c1ea..7fd9b379e1f24ef6329d9ed5c21ef02c3654c7f5 100644
--- a/dev/tests/js/spec/assets/apply/index.js
+++ b/dev/tests/js/jasmine/assets/apply/index.js
@@ -3,7 +3,7 @@
  * See COPYING.txt for license details.
  */
 define([
-    'tests/tools',
+    'tests/assets/tools',
     'tests/assets/apply/components/fn',
     'text!./config.json',
     'text!./templates/node.html'
diff --git a/dev/tests/js/spec/assets/apply/templates/node.html b/dev/tests/js/jasmine/assets/apply/templates/node.html
similarity index 100%
rename from dev/tests/js/spec/assets/apply/templates/node.html
rename to dev/tests/js/jasmine/assets/apply/templates/node.html
diff --git a/dev/tests/js/spec/assets/jsbuild/config.js b/dev/tests/js/jasmine/assets/jsbuild/config.js
similarity index 100%
rename from dev/tests/js/spec/assets/jsbuild/config.js
rename to dev/tests/js/jasmine/assets/jsbuild/config.js
diff --git a/dev/tests/js/spec/assets/jsbuild/external.js b/dev/tests/js/jasmine/assets/jsbuild/external.js
similarity index 100%
rename from dev/tests/js/spec/assets/jsbuild/external.js
rename to dev/tests/js/jasmine/assets/jsbuild/external.js
diff --git a/dev/tests/js/spec/assets/jsbuild/local.js b/dev/tests/js/jasmine/assets/jsbuild/local.js
similarity index 100%
rename from dev/tests/js/spec/assets/jsbuild/local.js
rename to dev/tests/js/jasmine/assets/jsbuild/local.js
diff --git a/dev/tests/js/spec/assets/script/config.json b/dev/tests/js/jasmine/assets/script/config.json
similarity index 100%
rename from dev/tests/js/spec/assets/script/config.json
rename to dev/tests/js/jasmine/assets/script/config.json
diff --git a/dev/tests/js/spec/assets/script/index.js b/dev/tests/js/jasmine/assets/script/index.js
similarity index 93%
rename from dev/tests/js/spec/assets/script/index.js
rename to dev/tests/js/jasmine/assets/script/index.js
index 031241f4c2d3e8beb24b9d7ee2373daa058a03f7..e9519e7ebe1e6b4e398ef06b68e59facb73850f5 100644
--- a/dev/tests/js/spec/assets/script/index.js
+++ b/dev/tests/js/jasmine/assets/script/index.js
@@ -3,7 +3,7 @@
  * See COPYING.txt for license details.
  */
 define([
-    'tests/tools',
+    'tests/assets/tools',
     'text!./config.json',
     'text!./templates/selector.html',
     'text!./templates/virtual.html'
diff --git a/dev/tests/js/spec/assets/script/templates/selector.html b/dev/tests/js/jasmine/assets/script/templates/selector.html
similarity index 100%
rename from dev/tests/js/spec/assets/script/templates/selector.html
rename to dev/tests/js/jasmine/assets/script/templates/selector.html
diff --git a/dev/tests/js/spec/assets/script/templates/virtual.html b/dev/tests/js/jasmine/assets/script/templates/virtual.html
similarity index 100%
rename from dev/tests/js/spec/assets/script/templates/virtual.html
rename to dev/tests/js/jasmine/assets/script/templates/virtual.html
diff --git a/dev/tests/js/spec/assets/text/config.js b/dev/tests/js/jasmine/assets/text/config.js
similarity index 50%
rename from dev/tests/js/spec/assets/text/config.js
rename to dev/tests/js/jasmine/assets/text/config.js
index fe8bc894e540567eb4948f04a683003932e39bda..0b0faa2d5cdd3eb68a7aeaccb5637f7c3eb97fcc 100644
--- a/dev/tests/js/spec/assets/text/config.js
+++ b/dev/tests/js/jasmine/assets/text/config.js
@@ -8,11 +8,11 @@ define(function () {
     return {
         local: {
             path: 'text!tests/assets/text/local.html',
-            result: '<span>Local Template</span>'
+            result: '<!--\n/**\n * Copyright © 2015 Magento. All rights reserved.\n * See COPYING.txt for license details.\n */\n-->\n<span>Local Template</span>'
         },
         external: {
             path: 'text!tests/assets/text/external.html',
-            result: '<span>External Template</span>'
+            result: '<!--\n/**\n * Copyright © 2015 Magento. All rights reserved.\n * See COPYING.txt for license details.\n */\n-->\n<span>External Template</span>'
         }
     };
 });
diff --git a/dev/tests/js/spec/assets/text/external.html b/dev/tests/js/jasmine/assets/text/external.html
similarity index 100%
rename from dev/tests/js/spec/assets/text/external.html
rename to dev/tests/js/jasmine/assets/text/external.html
diff --git a/dev/tests/js/spec/assets/text/local.html b/dev/tests/js/jasmine/assets/text/local.html
similarity index 100%
rename from dev/tests/js/spec/assets/text/local.html
rename to dev/tests/js/jasmine/assets/text/local.html
diff --git a/dev/tests/js/spec/tools.js b/dev/tests/js/jasmine/assets/tools.js
similarity index 100%
rename from dev/tests/js/spec/tools.js
rename to dev/tests/js/jasmine/assets/tools.js
diff --git a/dev/tests/js/jasmine/require.conf.js b/dev/tests/js/jasmine/require.conf.js
new file mode 100644
index 0000000000000000000000000000000000000000..88a0960aa1876bf8eca5c99e520f23965b83ea50
--- /dev/null
+++ b/dev/tests/js/jasmine/require.conf.js
@@ -0,0 +1,32 @@
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+'use strict';
+
+require.config({
+    baseUrl: './',
+    bundles: {
+        'mage/requirejs/static': [
+            'buildTools',
+            'jsbuild',
+            'statistician',
+            'text'
+        ]
+    },
+    paths: {
+        'tests': 'dev/tests/js/jasmine'
+    },
+    config: {
+        jsbuild: {
+            'dev/tests/js/jasmine/assets/jsbuild/local.js': 'define([], function () {\'use strict\'; return \'internal module\'; });'
+        },
+        text: {
+            'dev/tests/js/jasmine/assets/text/local.html': '<!--\n/**\n * Copyright © 2015 Magento. All rights reserved.\n * See COPYING.txt for license details.\n */\n-->\n<span>Local Template</span>'
+        }
+    },
+    deps: [
+        'mage/requirejs/static'
+    ]
+});
\ No newline at end of file
diff --git a/dev/tests/js/jasmine/spec_runner/index.js b/dev/tests/js/jasmine/spec_runner/index.js
new file mode 100644
index 0000000000000000000000000000000000000000..d63429aae1270559b0ef2af345d06d8dde170b8c
--- /dev/null
+++ b/dev/tests/js/jasmine/spec_runner/index.js
@@ -0,0 +1,62 @@
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+'use strict';
+
+var tasks = [],
+    _ = require('underscore');
+
+function init(grunt, options) {
+    var _                   = require('underscore'),
+        stripJsonComments   = require('strip-json-comments'),
+        path                = require('path'),
+        config,
+        themes;
+        
+    config = grunt.file.read(__dirname + '/settings.json');
+    config = stripJsonComments(config);
+    config = JSON.parse(config);
+
+    themes = require(path.resolve(process.cwd(), config.themes));
+
+    if (options.theme) {
+        themes = _.pick(themes, options.theme);
+    }
+
+    tasks = Object.keys(themes);
+
+    config.themes = themes;
+
+    enableTasks(grunt, config);
+}
+
+function enableTasks(grunt, config) {
+    var jasmine = require('./tasks/jasmine'),
+        connect = require('./tasks/connect');
+
+    jasmine.init(config);
+    connect.init(config);
+
+    grunt.initConfig({
+        jasmine: jasmine.getTasks(),
+        connect: connect.getTasks()
+    });
+}
+
+function getTasks() {
+    tasks = tasks.map(function (theme) {
+        return [
+            'connect:' + theme,
+            'jasmine:' + theme
+        ]
+    });
+
+    return _.flatten(tasks);
+}
+
+module.exports = {
+    init: init,
+    getTasks: getTasks
+};
\ No newline at end of file
diff --git a/dev/tests/js/jasmine/spec_runner/settings.json b/dev/tests/js/jasmine/spec_runner/settings.json
new file mode 100644
index 0000000000000000000000000000000000000000..109da479146da84882c4a6a68e8778db0b19701d
--- /dev/null
+++ b/dev/tests/js/jasmine/spec_runner/settings.json
@@ -0,0 +1,72 @@
+{
+    "host": "http://localhost:<%= port %>",
+    "port": 8000,
+    "root": "dev/tests/js/jasmine",
+
+    /**
+     * Path to themes configuration module. Relative to Magento root.
+     * This node is replaced by formatted theme configuration by 'dev/tests/jasmine/spec_runner' module
+     */
+    "themes": "dev/tools/grunt/configs/themes",
+    
+    "files": {
+        /**
+         * Path to RequireJS library. Relative to "server.base" config.
+         */
+        "requireJs": "requirejs/require.js",
+
+        /**
+         * Overridden "grunt-contrib-jasmine" SpecRunner template.
+         */
+        "template": "<%= root %>/spec_runner/template.html",
+
+        /**
+         * These files are included to the page in <head> right after "require.js" in declared sequence.
+         */
+        "requirejsConfigs": [
+            "pub/static/_requirejs/<%= area %>/<%= name %>/<%= locale %>/requirejs-config.js",
+            "<%= root %>/require.conf.js",
+            "<%= root %>/tests/lib/**/*.conf.js",
+            "<%= root %>/tests/app/code/**/base/**/*.conf.js",
+            "<%= root %>/tests/app/code/**/<%= area %>/**/*.conf.js",
+            "<%= root %>/tests/app/design/<%= area %>/<%= name %>/**/*.conf.js"
+        ],
+
+        /**
+         * Files that contain tests. These are loaded to the page via RequireJS after all RequireJS configuration files have been loaded to the page.
+         * The sequence is ignored.
+         */
+        "specs": [
+            "<%= root %>/tests/lib/**/*.test.js",
+            "<%= root %>/tests/app/code/**/base/**/*.test.js",
+            "<%= root %>/tests/app/code/**/<%= area %>/**/*.test.js",
+            "<%= root %>/tests/app/design/<%= area %>/<%= name %>/**/*.test.js"
+        ]
+    },
+    "server": {
+        /**
+         * Directory to serve files from
+         */
+        "base": "pub/static/<%= area %>/<%= name %>/<%= locale %>",
+
+        /**
+         * Strings, mentioned here are interpreted as regular expressions. Use this option to override server's
+         *     default behaviour and serve matched urls "as is" from Magento root.
+         */
+        "serveAsIs": [
+            "^\/_SpecRunner.html",
+            "^\/dev\/tests",
+            "^\/.grunt",
+            "^\/pub\/static"
+        ],
+        "options": {
+            /**
+             * All options mentioned here are defaults for "connect" grunt task.
+             * "debug" option enables server logs
+             * "keepalive" makes "connect" task pause with set up spec server, which you can fetch by %host%:%port%/_SpecRunner.html address in browser
+             */
+            "debug": false,
+            "keepalive": false
+        }
+    }
+}
\ No newline at end of file
diff --git a/dev/tests/js/jasmine/spec_runner/tasks/connect.js b/dev/tests/js/jasmine/spec_runner/tasks/connect.js
new file mode 100644
index 0000000000000000000000000000000000000000..d472f210d3d5fe1abd5e0ac3e68336819eb12f42
--- /dev/null
+++ b/dev/tests/js/jasmine/spec_runner/tasks/connect.js
@@ -0,0 +1,64 @@
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+'use strict';
+
+var tasks = {};
+
+function init(config) {
+    var serveStatic = require('serve-static'),
+        grunt       = require('grunt'),
+        _           = require('underscore'),
+        path        = require('path'),
+        ignoredPaths, middleware, themes, files, port;
+
+    port         = config.port;
+    files        = config.files;
+    themes       = config.themes;
+    ignoredPaths = config.server.serveAsIs;
+
+    function serveAsIs(path) {
+        return ignoredPaths.some(function (ignoredPath) {
+            return new RegExp(ignoredPath).test(path);
+        });
+    }
+
+    middleware = function (connect, options, middlewares) {
+        var server = serveStatic(process.cwd());
+
+        middlewares.unshift(function (req, res, next) {
+            var url = req.url;
+                
+            if (serveAsIs(url)) {
+                return server.apply(null, arguments);
+            }
+
+            return next();
+        });
+
+        return middlewares;
+    }
+
+    _.each(themes, function (themeData, themeName) {
+        var options = {
+            base: _.template(config.server.base)(themeData),
+            port: port++,
+            middleware: middleware
+        };
+
+        _.defaults(options, config.server.options);
+
+        tasks[themeName] = { options: options };
+    });
+}
+
+function getTasks() {
+    return tasks;
+}
+
+module.exports = {
+    init: init,
+    getTasks: getTasks
+};
\ No newline at end of file
diff --git a/dev/tests/js/jasmine/spec_runner/tasks/jasmine.js b/dev/tests/js/jasmine/spec_runner/tasks/jasmine.js
new file mode 100644
index 0000000000000000000000000000000000000000..9c2eaeb23996feb26f2f8f4819056cacdbc47bb0
--- /dev/null
+++ b/dev/tests/js/jasmine/spec_runner/tasks/jasmine.js
@@ -0,0 +1,65 @@
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+'use strict';
+
+var tasks = {},
+    _ = require('underscore');
+
+function init(config) {
+    var grunt  = require('grunt'),
+        expand = grunt.file.expand.bind(grunt.file),
+        themes, root, host, port, files;
+
+    root         = config.root;
+    port         = config.port;
+    files        = config.files;
+    host         = _.template(config.host)({ port: port });
+    themes       = config.themes;
+
+    _.each(themes, function (themeData, themeName) {
+        var specs,
+            configs,
+            render;
+
+        _.extend(themeData, { root: root });
+
+        render  = renderTemplate.bind(null, themeData);
+        specs   = files.specs.map(render);
+        specs   = expand(specs).map(cutJsExtension);
+        configs = files.requirejsConfigs.map(render);
+
+        tasks[themeName] = {
+            src: configs,
+            options: {
+                host: host,
+                template: render(files.template),
+                vendor: files.requireJs,
+
+                /**
+                 * @todo rename "helpers" to "specs" (implies overriding grunt-contrib-jasmine code)
+                 */
+                helpers: specs
+            }
+        }
+    });
+}
+
+function renderTemplate(data, template) {
+    return _.template(template)(data);
+}
+
+function cutJsExtension(path) {
+    return path.replace(/\.js$/, '');
+}
+
+function getTasks() {
+    return tasks;
+}
+
+module.exports = {
+    init: init,
+    getTasks: getTasks
+};
\ No newline at end of file
diff --git a/dev/tests/js/jasmine/spec_runner/template.html b/dev/tests/js/jasmine/spec_runner/template.html
new file mode 100644
index 0000000000000000000000000000000000000000..39c2f843480509bbedaac4a21b11209c8bd45d62
--- /dev/null
+++ b/dev/tests/js/jasmine/spec_runner/template.html
@@ -0,0 +1,30 @@
+<!--
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+-->
+
+<!DOCTYPE html>
+<html>
+    <head>
+        <meta charset="utf-8" />
+        <title>Jasmine Spec Runner</title>
+        <link rel="shortcut icon" type="image/png" href="<%= temp %>/jasmine_favicon.png">
+        <% css.forEach(function (style) { %>
+        <link rel="stylesheet" href="<%= style %>">
+        <% }); %>
+        <% with (scripts) { %>
+            <% [].concat(polyfills, jasmine, boot, vendor, src, reporters).forEach(function (script) { %>
+            <script type="text/javascript" src="<%= script %>"></script>
+            <% }); %>
+        <% } %>
+        <script type="text/javascript">
+            var jasmineBoot = window.onload;
+            window.onload = null;
+
+            require(<%= JSON.stringify(scripts.helpers) %>, jasmineBoot);
+        </script>
+    </head>
+    <body></body>
+</html>
\ No newline at end of file
diff --git a/dev/tests/js/spec/integration/Magento/Msrp/frontend/js/msrp.js b/dev/tests/js/jasmine/tests/app/code/Magento/Msrp/frontend/js/msrp.test.js
similarity index 100%
rename from dev/tests/js/spec/integration/Magento/Msrp/frontend/js/msrp.js
rename to dev/tests/js/jasmine/tests/app/code/Magento/Msrp/frontend/js/msrp.test.js
diff --git a/dev/tests/js/spec/integration/Magento/PageCache/frontend/js/pageCache.js b/dev/tests/js/jasmine/tests/app/code/Magento/PageCache/frontend/js/page-cache.test.js
similarity index 100%
rename from dev/tests/js/spec/integration/Magento/PageCache/frontend/js/pageCache.js
rename to dev/tests/js/jasmine/tests/app/code/Magento/PageCache/frontend/js/page-cache.test.js
diff --git a/dev/tests/js/spec/unit/Magento/Ui/adminhtml/events.js b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/lib/events.test.js
similarity index 99%
rename from dev/tests/js/spec/unit/Magento/Ui/adminhtml/events.js
rename to dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/lib/events.test.js
index c97b375562b05bc3d08dd1f2a2684b86d37b0183..bc2f08748f45e25f476cd2b0494f988b9f2bf41f 100644
--- a/dev/tests/js/spec/unit/Magento/Ui/adminhtml/events.js
+++ b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/lib/events.test.js
@@ -2,6 +2,7 @@
  * Copyright © 2015 Magento. All rights reserved.
  * See COPYING.txt for license details.
  */
+
 define([
     'Magento_Ui/js/lib/events'
 ], function (EventBus) {
diff --git a/dev/tests/js/spec/integration/Magento/Ui/adminhtml/datepicker.js b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/lib/ko/bind/datepicker.test.js
similarity index 99%
rename from dev/tests/js/spec/integration/Magento/Ui/adminhtml/datepicker.js
rename to dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/lib/ko/bind/datepicker.test.js
index 48c728fcf2e283474aed3270a522ceee3398d16f..9868699682a9d4d7ff0df2634fb2fc4da1b6c921 100644
--- a/dev/tests/js/spec/integration/Magento/Ui/adminhtml/datepicker.js
+++ b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/lib/ko/bind/datepicker.test.js
@@ -2,6 +2,7 @@
  * Copyright © 2015 Magento. All rights reserved.
  * See COPYING.txt for license details.
  */
+
 define([
     'ko',
     'jquery',
diff --git a/dev/tests/js/spec/integration/lib/mage/apply.js b/dev/tests/js/jasmine/tests/lib/mage/apply.test.js
similarity index 98%
rename from dev/tests/js/spec/integration/lib/mage/apply.js
rename to dev/tests/js/jasmine/tests/lib/mage/apply.test.js
index 5e3965b5903b76d1089e57cf8d9e783065bfa342..a9d22ac7c67d2f59379da2e8aa67fdc503b8d2ae 100644
--- a/dev/tests/js/spec/integration/lib/mage/apply.js
+++ b/dev/tests/js/jasmine/tests/lib/mage/apply.test.js
@@ -4,7 +4,7 @@
  */
 define([
     'underscore',
-    'tests/tools',
+    'tests/assets/tools',
     'tests/assets/apply/index',
     'mage/apply/main'
 ], function (_, tools, config, mage) {
diff --git a/dev/tests/js/spec/integration/lib/mage/requirejs/static-jsbuild.js b/dev/tests/js/jasmine/tests/lib/mage/requirejs/static-jsbuild.test.js
similarity index 100%
rename from dev/tests/js/spec/integration/lib/mage/requirejs/static-jsbuild.js
rename to dev/tests/js/jasmine/tests/lib/mage/requirejs/static-jsbuild.test.js
diff --git a/dev/tests/js/spec/integration/lib/mage/requirejs/static-text.js b/dev/tests/js/jasmine/tests/lib/mage/requirejs/static-text.test.js
similarity index 100%
rename from dev/tests/js/spec/integration/lib/mage/requirejs/static-text.js
rename to dev/tests/js/jasmine/tests/lib/mage/requirejs/static-text.test.js
diff --git a/dev/tests/js/spec/unit/lib/mage/requirejs/statistician.js b/dev/tests/js/jasmine/tests/lib/mage/requirejs/statistician.test.js
similarity index 100%
rename from dev/tests/js/spec/unit/lib/mage/requirejs/statistician.js
rename to dev/tests/js/jasmine/tests/lib/mage/requirejs/statistician.test.js
diff --git a/dev/tests/js/spec/integration/lib/mage/scripts.js b/dev/tests/js/jasmine/tests/lib/mage/scripts.test.js
similarity index 98%
rename from dev/tests/js/spec/integration/lib/mage/scripts.js
rename to dev/tests/js/jasmine/tests/lib/mage/scripts.test.js
index 05c4c13388919dd9a0ffd482663cf9bb99eda4bc..3bfbd4b637ea97801570ff2a571e8e21c3009d9c 100644
--- a/dev/tests/js/spec/integration/lib/mage/scripts.js
+++ b/dev/tests/js/jasmine/tests/lib/mage/scripts.test.js
@@ -3,7 +3,7 @@
  * See COPYING.txt for license details.
  */
 define([
-    'tests/tools',
+    'tests/assets/tools',
     'tests/assets/script/index',
     'mage/apply/scripts'
 ], function (tools, config, processScripts) {
diff --git a/dev/tests/js/spec/unit/lib/mage/template.js b/dev/tests/js/jasmine/tests/lib/mage/template.test.js
similarity index 100%
rename from dev/tests/js/spec/unit/lib/mage/template.js
rename to dev/tests/js/jasmine/tests/lib/mage/template.test.js
diff --git a/dev/tests/js/spec/integration/config/adminhtml.js b/dev/tests/js/spec/integration/config/adminhtml.js
deleted file mode 100644
index a9a60a43acad7e385f816e2ddce011f2cf682167..0000000000000000000000000000000000000000
--- a/dev/tests/js/spec/integration/config/adminhtml.js
+++ /dev/null
@@ -1,10 +0,0 @@
-/**
- * Copyright © 2015 Magento. All rights reserved.
- * See COPYING.txt for license details.
- */
-
-require.config({
-    paths: {
-        'jquery/ui': 'jquery/jquery-ui-1.9.2'
-    }
-});
diff --git a/dev/tests/js/spec/integration/config/frontend.js b/dev/tests/js/spec/integration/config/frontend.js
deleted file mode 100644
index 8791f57a90cfd289dbad10f5c140a34052b48139..0000000000000000000000000000000000000000
--- a/dev/tests/js/spec/integration/config/frontend.js
+++ /dev/null
@@ -1,10 +0,0 @@
-/**
- * Copyright © 2015 Magento. All rights reserved.
- * See COPYING.txt for license details.
- */
-
-require.config({
-    paths: {
-        'jquery/ui': 'jquery/jquery-ui'    
-    }
-})
\ No newline at end of file
diff --git a/dev/tests/js/spec/integration/config/global.js b/dev/tests/js/spec/integration/config/global.js
deleted file mode 100644
index 0f9ac6f574916eec9b0ee5707333c4461eece39e..0000000000000000000000000000000000000000
--- a/dev/tests/js/spec/integration/config/global.js
+++ /dev/null
@@ -1,27 +0,0 @@
-/**
- * Copyright © 2015 Magento. All rights reserved.
- * See COPYING.txt for license details.
- */
-require.config({
-    bundles: {
-        'mage/requirejs/static': [
-            'jsbuild',
-            'text',
-            'buildTools'
-        ]
-    },
-    config: {
-        jsbuild: {
-            'dev/tests/js/spec/assets/jsbuild/local.js': 'define([], function () {\'use strict\'; return \'internal module\'; });'
-        },
-        text: {
-            'dev/tests/js/spec/assets/text/local.html': '<span>Local Template</span>'
-        }
-    },
-    deps: [
-        'mage/requirejs/static'
-    ],
-    paths: {
-        'jquery/ui': 'jquery/jquery-ui'
-    }
-});
diff --git a/dev/tests/js/spec/require.config.js b/dev/tests/js/spec/require.config.js
deleted file mode 100644
index 2df785bb9962a997e3a19e838f834a8b1e984340..0000000000000000000000000000000000000000
--- a/dev/tests/js/spec/require.config.js
+++ /dev/null
@@ -1,16 +0,0 @@
-/**
- * Copyright © 2015 Magento. All rights reserved.
- * See COPYING.txt for license details.
- */
-
-require.config({
-    paths: {
-        'ko': 'ko/ko',
-        'domReady': 'requirejs/domReady',
-        'text': 'requirejs/text',
-        'tests': 'dev/tests/js/spec'
-    },
-    shim: {
-        'jquery/ui': ['jquery']
-    }
-});
diff --git a/dev/tests/js/spec/shim.js b/dev/tests/js/spec/shim.js
deleted file mode 100644
index 0cbe7edb109dee558040426db6c8d14678ff7b9c..0000000000000000000000000000000000000000
--- a/dev/tests/js/spec/shim.js
+++ /dev/null
@@ -1,46 +0,0 @@
-/**
- * Copyright © 2015 Magento. All rights reserved.
- * See COPYING.txt for license details.
- */
-(function () {
-    'use strict';
-
-    var Ap = Array.prototype,
-        slice = Ap.slice,
-        Fp = Function.prototype;
-
-    if (!Fp.bind) {
-        /**
-         * PhantomJS doesn't support Function.prototype.bind natively, so
-         * polyfill it whenever this module is required.
-         *
-         * @param  {*} context
-         * @return {Function}
-         */
-        Fp.bind = function (context) {
-            var func = this,
-                args = slice.call(arguments, 1);
-                
-            function bound() {
-                var invokedAsConstructor = func.prototype && (this instanceof func);
-
-                return func.apply(
-                    // Ignore the context parameter when invoking the bound function
-                    // as a constructor. Note that this includes not only constructor
-                    // invocations using the new keyword but also calls to base class
-                    // constructors such as BaseClass.call(this, ...) or super(...).
-                    !invokedAsConstructor && context || this,
-                    args.concat(slice.call(arguments))
-                );
-            }
-
-            // The bound function must share the .prototype of the unbound
-            // function so that any object created by one constructor will count
-            // as an instance of both constructors.
-            bound.prototype = func.prototype;
-
-            return bound;
-        };
-    }
-
-})();
diff --git a/dev/tests/js/spec/unit/config/adminhtml.js b/dev/tests/js/spec/unit/config/adminhtml.js
deleted file mode 100644
index a9a60a43acad7e385f816e2ddce011f2cf682167..0000000000000000000000000000000000000000
--- a/dev/tests/js/spec/unit/config/adminhtml.js
+++ /dev/null
@@ -1,10 +0,0 @@
-/**
- * Copyright © 2015 Magento. All rights reserved.
- * See COPYING.txt for license details.
- */
-
-require.config({
-    paths: {
-        'jquery/ui': 'jquery/jquery-ui-1.9.2'
-    }
-});
diff --git a/dev/tests/js/spec/unit/config/frontend.js b/dev/tests/js/spec/unit/config/frontend.js
deleted file mode 100644
index f6ee8dc73b131128e9cb0709f9765b060cd2e90d..0000000000000000000000000000000000000000
--- a/dev/tests/js/spec/unit/config/frontend.js
+++ /dev/null
@@ -1,10 +0,0 @@
-/**
- * Copyright © 2015 Magento. All rights reserved.
- * See COPYING.txt for license details.
- */
-
-require.config({
-    paths: {
-        'jquery/ui': 'jquery/jquery-ui'
-    }
-});
diff --git a/dev/tests/js/spec/unit/config/global.js b/dev/tests/js/spec/unit/config/global.js
deleted file mode 100644
index c59abab9fc575c4c8b3c99e8ee3422c53523a79a..0000000000000000000000000000000000000000
--- a/dev/tests/js/spec/unit/config/global.js
+++ /dev/null
@@ -1,15 +0,0 @@
-/**
- * Copyright © 2015 Magento. All rights reserved.
- * See COPYING.txt for license details.
- */
-
-require.config({
-    bundles: {
-        'mage/requirejs/static': [
-            'buildTools'
-        ]
-    },
-    deps: [
-        'mage/requirejs/static'
-    ]
-});
diff --git a/dev/tests/static/testsuite/Magento/Test/Js/_files/blacklist/core.txt b/dev/tests/static/testsuite/Magento/Test/Js/_files/blacklist/core.txt
index 2ba58c46a10ad6d2ce222f759e2baa6051c067b0..9210d15d05df6a7a82ce4f6c5f1b312bf54ccf01 100644
--- a/dev/tests/static/testsuite/Magento/Test/Js/_files/blacklist/core.txt
+++ b/dev/tests/static/testsuite/Magento/Test/Js/_files/blacklist/core.txt
@@ -12,8 +12,8 @@ app/code/Magento/Sales/view/adminhtml/web/order/giftoptions_tooltip.js
 app/code/Magento/Shipping/view/adminhtml/web/order/packaging.js
 app/code/Magento/Theme/view/frontend/web/menu.js
 app/code/Magento/Variable/view/adminhtml/web/variables.js
-dev/tests/js/testsuite/mage/translate_inline_vde/translate-inline-vde-test.js
-dev/tests/js/framework/qunit
+dev/tests/js/JsTestDriver/testsuite/mage/translate_inline_vde/translate-inline-vde-test.js
+dev/tests/js/JsTestDriver/framework/qunit
 lib/web/mage/adminhtml
 lib/web/mage/captcha.js
 lib/web/legacy-build.min.js
diff --git a/dev/tools/grunt/configs/jasmine.js b/dev/tools/grunt/configs/jasmine.js
deleted file mode 100644
index 75c33b5d92f98e3df5e7e4d9922a9cb6cc0d59c8..0000000000000000000000000000000000000000
--- a/dev/tools/grunt/configs/jasmine.js
+++ /dev/null
@@ -1,26 +0,0 @@
-/**
- * Copyright © 2015 Magento. All rights reserved.
- * See COPYING.txt for license details.
- */
-
-'use strict';
-
-var path = require('path');
-
-module.exports = function (grunt) {
-    var file = path.join(process.cwd(), 'dev/tests/js/framework/spec_runner'),
-        specRunner = require(file)(grunt);
-
-    return {
-        options: {
-            template: require('grunt-template-jasmine-requirejs'),
-            ignoreEmpty: true
-        },
-        'lib-unit':               specRunner.configure('unit', 'lib', 8080),
-        'lib-integration':        specRunner.configure('integration', 'lib', 8080),
-        'backend-unit':           specRunner.configure('unit', 'adminhtml', 8000),
-        'backend-integration':    specRunner.configure('integration', 'adminhtml', 8000),
-        'frontend-unit':          specRunner.configure('unit', 'frontend', 3000),
-        'frontend-integration':   specRunner.configure('integration', 'frontend', 3000)
-    };
-};
diff --git a/dev/tools/grunt/configs/specRunner.json b/dev/tools/grunt/configs/specRunner.json
deleted file mode 100644
index 0d47c57e101a2b121e6ea5f0f67c87ca2191b70e..0000000000000000000000000000000000000000
--- a/dev/tools/grunt/configs/specRunner.json
+++ /dev/null
@@ -1,24 +0,0 @@
-{
-    "options": {
-        "shareDir": "base"
-    },
-    "backend": {
-        "options": {
-            "port": 8000,
-            "areaDir": "adminhtml",
-            "theme": "backend"
-        }
-    },
-    "frontend": {
-        "options": {
-            "port": 3000,
-            "areaDir": "frontend",
-            "theme": "blank"
-        }
-    },
-    "lib": {
-        "options": {
-            "port": 8080
-        }
-    }
-}
\ No newline at end of file
diff --git a/dev/tools/grunt/tasks/deploy.js b/dev/tools/grunt/tasks/deploy.js
new file mode 100644
index 0000000000000000000000000000000000000000..bff8b1fabecaf8d9774a79aa3134c7163847ca30
--- /dev/null
+++ b/dev/tools/grunt/tasks/deploy.js
@@ -0,0 +1,35 @@
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+module.exports = function (grunt) {
+    'use strict';
+
+    var exec    = require('child_process').execSync,
+        spawn   = require('child_process').spawn,
+        log     = grunt.log.write,
+        ok      = grunt.log.ok,
+        error   = grunt.log.error;
+
+    grunt.registerTask('deploy', function () {
+        var deploy,
+            done = this.async();
+
+        log('Cleaning "pub/static"...');
+        exec('rm -rf pub/static/*');
+        ok('"pub/static" is empty.');
+
+        log('Deploying Magento application...');
+        deploy = spawn('php', ['dev/tools/Magento/Tools/View/deploy.php']);
+
+        deploy.stdout.on('data', function (data) {
+            log(data);
+        });
+
+        deploy.stdin.on('data', function (data) {
+            error(data);
+        });
+
+        deploy.on('close', done);
+    });
+};
diff --git a/package.json b/package.json
index 12ffc19d59651831c958e793c61e221f076988c0..eaee4e18e2484b09731ec9a8e2a165eef83ae29c 100644
--- a/package.json
+++ b/package.json
@@ -9,11 +9,11 @@
   },
   "homepage": "http://magento.com/",
   "devDependencies": {
-    "connect": "^3.3.3",
     "grunt": "^0.4.5",
     "grunt-autoprefixer": "^2.0.0",
     "grunt-banner": "^0.3.1",
     "grunt-contrib-clean": "^0.6.0",
+    "grunt-contrib-connect": "^0.9.0",
     "grunt-contrib-cssmin": "^0.10.0",
     "grunt-contrib-imagemin": "^0.9.2",
     "grunt-contrib-jasmine": "^0.8.1",
@@ -28,6 +28,7 @@
     "morgan": "^1.5.0",
     "node-minify": "^1.0.1",
     "serve-static": "^1.7.1",
+    "strip-json-comments": "^1.0.2",
     "time-grunt": "^1.0.0",
     "underscore": "^1.7.0"
   },