From 39e1a3fb4444d5cabc4d077bef8d39daf627dd42 Mon Sep 17 00:00:00 2001 From: Ryan Jones Date: Sun, 4 Dec 2016 20:53:06 -0800 Subject: [PATCH] add client-side test runner --- interface/src/ui/TestingDialog.cpp | 26 +++++---- interface/src/ui/TestingDialog.h | 29 ++++++---- .../developer/libraries/jasmine/hifi-boot.js | 48 ++++++++++++----- .../tests/{ => unit_tests}/avatarUnitTests.js | 6 +-- .../tests/{ => unit_tests}/bindUnitTest.js | 15 +++--- .../tests/unit_tests/entityUnitTests.js | 53 +++++++++++++++++++ .../developer/tests/unit_tests/testRunner.js | 13 +++++ 7 files changed, 145 insertions(+), 45 deletions(-) rename scripts/developer/tests/{ => unit_tests}/avatarUnitTests.js (93%) rename scripts/developer/tests/{ => unit_tests}/bindUnitTest.js (61%) create mode 100644 scripts/developer/tests/unit_tests/entityUnitTests.js create mode 100644 scripts/developer/tests/unit_tests/testRunner.js diff --git a/interface/src/ui/TestingDialog.cpp b/interface/src/ui/TestingDialog.cpp index 0a4435e13e..6f08bf87cf 100644 --- a/interface/src/ui/TestingDialog.cpp +++ b/interface/src/ui/TestingDialog.cpp @@ -9,7 +9,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include +#include "ScriptEngines.h" #include "ui/TestingDialog.h" #include "Application.h" @@ -17,19 +17,23 @@ TestingDialog::TestingDialog(QWidget* parent) : QDialog(parent, Qt::Window | Qt::WindowCloseButtonHint | Qt::WindowStaysOnTopHint) { - DependencyManager::get()->loadOneScript(qApp->applicationDirPath() + testRunnerRelativePath); - this->setWindowTitle(windowLabel); + setAttribute(Qt::WA_DeleteOnClose); + setWindowTitle(windowLabel); + + _console = new JSConsole(this); + _console->setFixedHeight(TESTING_CONSOLE_HEIGHT); + + auto _engines = DependencyManager::get(); + _engine = _engines->loadScript(qApp->applicationDirPath() + testRunnerRelativePath); + _console->setScriptEngine(_engine); + connect(_engine, &ScriptEngine::finished, this, &TestingDialog::onTestingFinished); } TestingDialog::~TestingDialog() { - // TODO: Clean up here? + delete _console; } -void TestingDialog::reject() { - this->QDialog::close(); -} - -void TestingDialog::closeEvent(QCloseEvent* event) { - this->QDialog::closeEvent(event); - emit closed(); +void TestingDialog::onTestingFinished(const QString& scriptPath) { + _engine = NULL; + _console->setScriptEngine(NULL); } diff --git a/interface/src/ui/TestingDialog.h b/interface/src/ui/TestingDialog.h index 97da4731f9..1dc916a6f7 100644 --- a/interface/src/ui/TestingDialog.h +++ b/interface/src/ui/TestingDialog.h @@ -1,10 +1,24 @@ +// +// TestingDialog.h +// interface/src/ui +// +// Created by Ryan Jones on 12/3/2016. +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + #ifndef hifi_TestingDialog_h #define hifi_TestingDialog_h #include +#include "ScriptEngine.h" +#include "JSConsole.h" -const QString windowLabel = "Testing Dialog"; -const QString testRunnerRelativePath = "/scripts/developer/tests/bindUnitTest.js"; +const QString windowLabel = "Client Script Tests"; +const QString testRunnerRelativePath = "/scripts/developer/tests/unit_tests/testRunner.js"; +const unsigned int TESTING_CONSOLE_HEIGHT = 400; class TestingDialog : public QDialog { Q_OBJECT @@ -12,14 +26,11 @@ public: TestingDialog(QWidget* parent); ~TestingDialog(); -signals: - void closed(); + void onTestingFinished(const QString& scriptPath); -public slots: - void reject() override; - -protected: - void closeEvent(QCloseEvent*) override; +private: + JSConsole* _console; + ScriptEngine* _engine; }; #endif \ No newline at end of file diff --git a/scripts/developer/libraries/jasmine/hifi-boot.js b/scripts/developer/libraries/jasmine/hifi-boot.js index 0c66a925af..f490a3618f 100644 --- a/scripts/developer/libraries/jasmine/hifi-boot.js +++ b/scripts/developer/libraries/jasmine/hifi-boot.js @@ -1,27 +1,50 @@ - (function() { + var BALLOT_X = '✗'; + var CHECKMARK = '✓'; + var DOWN_RIGHT_ARROW = '↳'; + var PASSED = 'passed'; + var lastSpecStartTime; function ConsoleReporter(options) { + var startTime = new Date().getTime(); + var errorCount = 0; this.jasmineStarted = function (obj) { - print("jasmineStarted: numSpecs = " + obj.totalSpecsDefined); + print('Jasmine started with ' + obj.totalSpecsDefined + ' tests.'); }; this.jasmineDone = function (obj) { - print("jasmineDone"); + var ERROR = errorCount === 1 ? 'error' : 'errors'; + var endTime = new Date().getTime(); + print('
'); + if (errorCount === 0) { + print ('All tests passed!'); + } else { + print('Tests completed with ' + + errorCount + ' ' + ERROR + '.'); + } + print('Tests completed in ' + (endTime - startTime) + 'ms.'); }; this.suiteStarted = function(obj) { - print("suiteStarted: \"" + obj.fullName + "\""); + print(obj.fullName); }; this.suiteDone = function(obj) { - print("suiteDone: \"" + obj.fullName + "\" " + obj.status); + print(''); }; this.specStarted = function(obj) { - print("specStarted: \"" + obj.fullName + "\""); + lastSpecStartTime = new Date().getTime(); }; this.specDone = function(obj) { - print("specDone: \"" + obj.fullName + "\" " + obj.status); + var specEndTime = new Date().getTime(); + var symbol = obj.status === PASSED ? + '' + CHECKMARK + '' : + '' + BALLOT_X + ''; + print('... ' + obj.fullName + ' ' + symbol + ' ' + '[' + + (specEndTime - lastSpecStartTime) + 'ms]'); - var i, l = obj.failedExpectations.length; - for (i = 0; i < l; i++) { - print(" " + obj.failedExpectations[i].message); + var specErrors = obj.failedExpectations.length; + errorCount += specErrors; + for (var i = 0; i < specErrors; i++) { + print('' + DOWN_RIGHT_ARROW + + ' ' + + obj.failedExpectations[i].message + ''); } }; return this; @@ -44,10 +67,11 @@ function extend(destination, source) { for (var property in source) { - destination[property] = source[property]; + if (source.hasOwnProperty(property)) { + destination[property] = source[property]; + } } return destination; } - }()); diff --git a/scripts/developer/tests/avatarUnitTests.js b/scripts/developer/tests/unit_tests/avatarUnitTests.js similarity index 93% rename from scripts/developer/tests/avatarUnitTests.js rename to scripts/developer/tests/unit_tests/avatarUnitTests.js index 29e3ad0588..2ff0f3b092 100644 --- a/scripts/developer/tests/avatarUnitTests.js +++ b/scripts/developer/tests/unit_tests/avatarUnitTests.js @@ -1,7 +1,4 @@ -Script.include("../libraries/jasmine/jasmine.js"); -Script.include("../libraries/jasmine/hifi-boot.js"); - // Art3mis var DEFAULT_AVATAR_URL = "https://hifi-metaverse.s3-us-west-1.amazonaws.com/marketplace/contents/e76946cc-c272-4adf-9bb6-02cde0a4b57d/8fd984ea6fe1495147a3303f87fa6e23.fst?1460131758"; @@ -17,6 +14,7 @@ describe("MyAvatar", function () { // wait until we are finished loading var id = Script.setInterval(function () { + print(MyAvatar.jointNames.length); if (MyAvatar.jointNames.length == 72) { // assume we are finished loading. Script.clearInterval(id); @@ -55,5 +53,3 @@ describe("MyAvatar", function () { }); -jasmine.getEnv().execute(); - diff --git a/scripts/developer/tests/bindUnitTest.js b/scripts/developer/tests/unit_tests/bindUnitTest.js similarity index 61% rename from scripts/developer/tests/bindUnitTest.js rename to scripts/developer/tests/unit_tests/bindUnitTest.js index 95fd497916..609487a30b 100644 --- a/scripts/developer/tests/bindUnitTest.js +++ b/scripts/developer/tests/unit_tests/bindUnitTest.js @@ -1,9 +1,12 @@ -Script.include('../libraries/jasmine/jasmine.js'); -Script.include('../libraries/jasmine/hifi-boot.js'); -Script.include('../../system/libraries/utils.js'); +Script.include('../../../system/libraries/utils.js'); describe('Bind', function() { - it('functions should have bind available', function() { + it('exists for functions', function() { + var FUNC = 'function'; + expect(typeof(function() {}.bind)).toEqual(FUNC); + }); + + it('should allow for setting context of this', function() { var foo = 'bar'; function callAnotherFn(anotherFn) { @@ -22,10 +25,6 @@ describe('Bind', function() { var instance = new TestConstructor(); - expect(typeof(instance.doSomething.bind) !== 'undefined'); expect(instance.doSomething()).toEqual(foo); }); }); - -jasmine.getEnv().execute(); -Script.stop(); \ No newline at end of file diff --git a/scripts/developer/tests/unit_tests/entityUnitTests.js b/scripts/developer/tests/unit_tests/entityUnitTests.js new file mode 100644 index 0000000000..ad1606c4e9 --- /dev/null +++ b/scripts/developer/tests/unit_tests/entityUnitTests.js @@ -0,0 +1,53 @@ +describe('Entity', function() { + var center = Vec3.sum( + MyAvatar.position, + Vec3.multiply(3, Quat.getFront(Camera.getOrientation())) + ); + var boxEntity; + var boxProps = { + type: 'Box', + color: { + red: 255, + green: 255, + blue: 255, + }, + position: center, + dimensions: { + x: 1, + y: 1, + z: 1, + }, + }; + + beforeEach(function() { + boxEntity = Entities.addEntity(boxProps); + }); + + afterEach(function() { + Entities.deleteEntity(boxEntity); + boxEntity = null; + }); + + it('can be added programmatically', function() { + expect(typeof(boxEntity)).toEqual('string'); + }); + + it('instantiates itself correctly', function() { + var props = Entities.getEntityProperties(boxEntity); + expect(props.type).toEqual(boxProps.type); + }); + + it('can be modified after creation', function() { + var newPos = { + x: boxProps.position.x, + y: boxProps.position.y, + z: boxProps.position.z + 1.0, + }; + Entities.editEntity(boxEntity, { + position: newPos, + }); + + var props = Entities.getEntityProperties(boxEntity); + expect(Math.round(props.position.z)).toEqual(Math.round(newPos.z)); + }); +}); \ No newline at end of file diff --git a/scripts/developer/tests/unit_tests/testRunner.js b/scripts/developer/tests/unit_tests/testRunner.js new file mode 100644 index 0000000000..31d83cd986 --- /dev/null +++ b/scripts/developer/tests/unit_tests/testRunner.js @@ -0,0 +1,13 @@ +// Include testing library +Script.include('../../libraries/jasmine/jasmine.js'); +Script.include('../../libraries/jasmine/hifi-boot.js') + +// Include unit tests +// FIXME: Figure out why jasmine done() is not working. +// Script.include('avatarUnitTests.js'); +Script.include('bindUnitTest.js'); +Script.include('entityUnitTests.js'); + +// Run the tests +jasmine.getEnv().execute(); +Script.stop();