add client-side test runner

This commit is contained in:
Ryan Jones 2016-12-04 20:53:06 -08:00
parent 3eecba77f2
commit 39e1a3fb44
7 changed files with 145 additions and 45 deletions

View file

@ -9,7 +9,7 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <ScriptEngines.h>
#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<ScriptEngines>()->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<ScriptEngines>();
_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);
}

View file

@ -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 <QDialog>
#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

View file

@ -1,27 +1,50 @@
(function() {
var BALLOT_X = '&#10007;';
var CHECKMARK = '&#10003;';
var DOWN_RIGHT_ARROW = '&#8627;';
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('<hr />');
if (errorCount === 0) {
print ('<span style="color:green">All tests passed!</span>');
} else {
print('<span style="color:red">Tests completed with ' +
errorCount + ' ' + ERROR + '.<span>');
}
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 ?
'<span style="color:green">' + CHECKMARK + '</span>' :
'<span style="color:red">' + BALLOT_X + '</span>';
print('... ' + obj.fullName + ' ' + symbol + ' ' + '<span style="color:orange">[' +
(specEndTime - lastSpecStartTime) + 'ms]</span>');
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('<span style="margin-right:0.5em"></span>' + DOWN_RIGHT_ARROW +
'<span style="color:red"> ' +
obj.failedExpectations[i].message + '</span>');
}
};
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;
}
}());

View file

@ -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();

View file

@ -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();

View file

@ -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));
});
});

View file

@ -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();