I am using the sht_rails gem to render handlebars templates in my Rails 3.2/Backbone App.
I'm hoping to use this .handlebars template in both the backbone and rails portion of the app, but so far I just have it working in the backbone.
I'm using it like so:
class MyApp.views.MyView extends MyApp.views.BaseView
template: SHT['templates/feed_item']
render: ->
data = {}
#$el.html #template(data)
#
This works great in app, no problems at all, my handlebars template is looking sweet.
However, this is no good for my js testing (I'm using Jasmine with jasmine-headless-webkit)
This is what happens:
$ jasmine-headless-webkit
ReferenceError: Can't find variable: SHT
This makes total sense, as it seems that the sht_rails gem registers the SHT variable, however, it doesn't seem to do this in when I test.
Is there a good way to register the SHT variable when running jhw? or jasmine by itself? I don't even need the template to render for my test, just knowing that the template is called would be enough for me. But for now, all my jasmine tests are broken until I figure out how to register this SHT.
Thanks!
We faced the same problem when using jade templates in our Rails 3.2/Backbone/Marionette app via the tilt-jade gem (hence the JST variable in the code samples below). Our solution was to create a template abstraction and then use Jasmine spies to fake a response during spec execution. This approach also allows us to test template usage, construction, etc.
Spies In General
In case you're unfamiliar with Jasmine spies:
Jasmine integrates 'spies' that permit many spying, mocking, and faking behaviors. A 'spy' replaces the function it is spying on.
Abstraction
Create the abstraction:
YourApp.tpl = function(key, data) {
var path = "templates";
path += key.charAt(0) === "/" ? key : "/" + key;
var templateFn = JST[path];
if(!templateFn) {
throw new Error('Template "' + path + '" not found');
}
if(typeof templateFn !== "function") {
throw new Error('Template "' + path + '" was a ' + typeof(templateFn) + '. Type "function" expected');
}
return templateFn(data);
};
and the requisite monkeypatch:
// MONKEYPATCH - Overriding Renderer to use YourApp template function
Marionette.Renderer = {
render: function(template, model){
return YourApp.tpl(template, model);
}
};
Template Abstraction Spy (spec_helper.js)
Now we can spy as follows:
spyOn(YourApp, 'tpl').andCallFake(function(key, data) {
return function() {
return key;
};
});
Bonus
Since we are spying on the YourApp.tpl function, we can also test against it:
expect(YourApp.tpl).toHaveBeenCalledWith("your_template", { model: model, property: value });
Addendum
If you don't already know about the jasmine-headless-webkit --runner-out flag and are debugging your Jasmine specs in the wilderness, check out this post to see how to generate a runner output report with a full backtrace for any failures.
Related
First, I came from a .NET background so please excuse my lack of groovy lingo. Back when I was in a .NET shop, we were using TypeScript with C# to build web apps. In our controllers, we would always receive/respond with DTOs (data xfer objects). This got to be quite the headache every time you create/modify a DTO you had to update the TypeScript interface (the d.ts file) that corresponded to it.
So we created a little app (a simple exe) that loaded the dll from the webapp into it, then reflected over it to find the DTOs (filtering by specific namespaces), and parse through them to find each class name within, their properties, and their properties' data types, generate that information into a string, and finally saved as into a d.ts file.
This app was then configured to run on every build of the website. That way, when you go to run/debug/build the website, it would update your d.ts files automatically - which made working with TypeScript that much easier.
Long story short, how could I achieve this with a Grails Website if I were to write a simple groovy app to generate the d.ts that I want?
-- OR --
How do I get the IDE (ex IntelliJ) to run a groovy file (that is part of the app) that does this generation post-build?
I did find this but still need a way to run on compile:
Groovy property iteration
class Foo {
def feck = "fe"
def arse = "ar"
def drink = "dr"
}
class Foo2 {
def feck = "fe2"
def arse = "ar2"
def drink = "dr2"
}
def f = new Foo()
def f2 = new Foo2()
f2.properties.each { prop, val ->
if(prop in ["metaClass","class"]) return
if(f.hasProperty(prop)) f[prop] = val
}
assert f.feck == "fe2"
assert f.arse == "ar2"
assert f.drink == "dr2"
I've been able to extract the Domain Objects and their persistent fields via the following Gant script:
In scripts/Props.groovy:
import static groovy.json.JsonOutput.*
includeTargets << grailsScript("_GrailsBootstrap")
target(props: "Lists persistent properties for each domain class") {
depends(loadApp)
def propMap = [:].withDefault { [] }
grailsApp.domainClasses.each {
it?.persistentProperties?.each { prop ->
if (prop.hasProperty('name') && prop.name) {
propMap[it.clazz.name] << ["${prop.name}": "${prop.getType()?.name}"]
}
}
}
// do any necessary file I/O here (just printing it now as an example)
println prettyPrint(toJson(propMap))
}
setDefaultTarget(props)
This can be run via the command line like so:
grails props
Which produces output like the following:
{
"com.mycompany.User": [
{ "type": "java.lang.String" },
{ "username": "java.lang.String" },
{ "password": "java.lang.String" }
],
"com.mycompany.Person": [
{ "name": "java.lang.String" },
{ "alive": "java.lang.Boolean" }
]
}
A couple of drawbacks to this approach is that we don't get any transient properties and I'm not exactly sure how to hook this into the _Events.groovy eventCompileEnd event.
Thanks Kevin! Just wanted to mention, in order to get this to run, here are a few steps I had to make sure to do in my case that I thought I would share:
-> Open up the grails BuildConfig.groovy
-> Change tomcat from build to compile like this:
plugins {
compile ":tomcat:[version]"
}
-> Drop your Props.groovy into the scripts folder on the root (noting the path to the grails-app folder for reference)
[application root]/scripts/Props.groovy
[application root]/grails-app
-> Open Terminal
gvm use grails [version]
grails compile
grails Props
Note: I was using Grails 2.3.11 for the project I was running this on.
That gets everything in your script to run successfully for me. Now to modify the println portion to generate Typescript interfaces.
Will post a github link when it is ready so be sure to check back.
I'm writing a yeoman generator and want to check some prerequisites, for example a git being installed. I can easily check this using .exec, but how do i gracefully abort generator and report error to user? I searched docs, but it seems that i'm missing some obvious way to do it. Any hints?
Throwing exception will of course abort generator, but is it a best way? Maybe something more user friendly? Not all yeoman users are able to read js exceptions.
The current state of error handling in the popular generators is quite diverse:
in the most cases they just log the error and return from the action and let the subsequnt actions run and return 0 status code:
generator-karma's setupTravis method:
if (err) {
this.log.error('Could not open package.json for reading.', err);
done();
return;
}
or set a custom abort property on error and skip further actions with cheking on the abort property but still return 0 status code:
generator-jhipster's CloudFoundryGenerator:
CloudFoundryGenerator.prototype.checkInstallation = function checkInstallation() {
if(this.abort) return;
var done = this.async();
exec('cf --version', function (err) {
if (err) {
this.log.error('cloudfoundry\'s cf command line interface is not available. ' +
'You can install it via https://github.com/cloudfoundry/cli/releases');
this.abort = true;
}
done();
}.bind(this));
};
or manually end the process with process.exit:
generator-mobile's configuringmethod:
if (err) {
self.log.error(err);
process.exit(1);
}
However none of these methods provide a good way to signal to the environment that something went wrong except the last one but directly calling process.exit is a design smell.
Throwing an exception is also an option but this presents also the stackstrace to the user which is not always a good idea.
The best option would be use the Environment.error method, which has some nice advantages:
the Environment is exposed thorough the env property of the yeoman.generators.Base
an error event is emitted which is handled by the yo cli code
the execution will result in a non zero (error) status code which is override-able
by default yo will display only the message and no stacktrace
the stacktrace can be optionally displayed with providing the --debug built-in option when re-running the generator.
With using this technique your action method would look like this:
module.exports = generators.Base.extend({
method1: function () {
console.log('method 1 just ran');
this.env.error("something bad is happened");
console.log('this won't be executed');
},
method2: function () {
console.log('this won't be executed');
}
});
Attempting to run some jasmine specs (written in coffeescript), via the jasmine-rails gem, and I'm getting several jasmine errors. I'm using the andCallFake() method at several points in my code, and any specs which contain that method are failing. All of my other tests pass. At first I suspected it had something to do with the jasmine.mock-ajax library, but attempting to create a spy on a method which doesn't use that library causes the same error.
Example test code (coffee):
it 'should trigger an event on success', ->
validateSpy = jasmine.createSpy 'validate spy'
obj.on 'validated', validateSpy
$.ajax.isSpy = false
spyOn($, 'ajax').andCallFake(params) ->
params.success = true
And the error message:
TypeError: Object function () {
callTracker.track({
object: this,
args: Array.prototype.slice.apply(arguments)
});
return spyStrategy.exec.apply(this, arguments);
} has no method 'andCallFake'
I can spot one error in your test code: you need a space between andCallFake and (params)
So andCallFake (params)
I just tried this on coffeescript.org and you can see the difference
It looks like jasmine 2.0 removes the andCallFake method, and we slurped it up with a bundle update. Locking the jasmine-core gem down solved the problem.
I am trying to get Ranorex to output a text file which will look like the following:
Pass
74
The pass/fail result will be obtained based on whether the test running has passed or failed. The number will be hardcoded to all I need to do is store that in a variable and include it in the output.
I would have thought it would have been simple but I'm struggling to get any help from Ranorex. I though I might be able to use the reporting function, change the output file type and alter the report structure but that didn't work either.
Although I am used to Ranorex and writing my own user code, I am new to adapting it in this way.
All my user code is written in C#
Can anyone offer any assistance?
Thanks!
Edit: So I've now managed to get Ranorex to output a text file and I can put any text into it, including a string stored in a variable.
However I'm struggling to store the pass/fail result of my test in a string that I can output.
I've discovered a way to do this however it relies on the following:-
The user code must be in separate test
This separate test must exist in a sibling test case to the one your main test is in
Both this test case and the case containing your main test must both be part of a parent test case
For example:
Parent TC
.....-AddUser TC
.........-MAIN TEST
.....-AddUser FailCheck
.........-USER CODE
You can then set your AddUser TC to 'Continue with sibling on fail'
The user code is as follows:
public static void Output()
{
string result = "";
ITestCase iCase = TestSuite.Current.GetTestCase("Add_User_Test"); // The name of your Test Case
if(iCase.Status == Ranorex.Core.Reporting.ActivityStatus.Failed){
result = "Failed"; }
if(iCase.Status == Ranorex.Core.Reporting.ActivityStatus.Success){
result = "Passed"; }
int testrunID = 79;
using (StreamWriter writer =
new StreamWriter("testresult.txt"))
{
writer.WriteLine(testrunID);
writer.WriteLine(result);
}
}
This will take the testrunID (specific to each test case) and the result of the test and output it to a text file.
The idea is then to read in the file with a custom java application I've developed and push the data into a test case management program such as QA Complete which can mark tests as Passed/Failed automatically
You can run the test suite directly using the TestSuiteRunner.Run() method. This will allow you to look at the return value of that directly and output pass or failure based on the return value.
http://www.ranorex.com/Documentation/Ranorex/html/M_Ranorex_Core_Testing_TestSuiteRunner_Run.htm
if(TestSuiteRunner.Run(typeof({testSuiteclass}),{Command Line Arguments})==0)
{
File.WriteLine("success");
}
else
{
File.WriteLine("failure");
}
My requirement is to invoke some processing from a Jenkins build server, to determine whether the domain model has changed since the last build. I've come to the conclusion that the way forward is to write a script that will invoke a sequence of existing scripts from the db-migration plugin. Then I can invoke it in the step that calls test-app and war.
I've looked in the Grails doc, and at some of the db-migration scripts, and I find I'm stuck - have no idea where to start trying things. I'd be really grateful if someone could point me at any suitable sources. BTW, I'm a bit rusty in Grails. Started to teach myself two years ago via proof of concept project, which lasted 6 months. Then it was back to Eclipse rich client work. That might be part of my problem, though I never go involved in scripts.
One thing I need in the Jenkins evt is to get hold of the current SVN revision number being used for the build. Suggestions welcome.
Regards, John
Create a new script by running grails create-script scriptname. The database-migration plugins scripts are configured to be easily reused. There are is a lot of shared code in _DatabaseMigrationCommon.groovy and each script defines one target with a unique name. So you can import either the shared script or any standalone script (or multiple scripts) and call the targets like they're methods.
By default the script generated by create-script "imports" the _GrailsInit script via includeTargets << grailsScript("_GrailsInit") and you can do the same, taking advantage of the magic variables that point at installed plugins' directories:
includeTargets << new File("$databaseMigrationPluginDir/scripts/DbmGenerateChangelog.groovy")
If you do this you can remove the include of _GrailsInit since it's already included, but if you don't that's fine since Grails only includes files once.
Then you can define your target and call any of the plugin's targets. The targets cannot accept parameters, but you can add data to the argsMap (this is a map Grails creates from the parsed commandline arguments) to simulate user-specified args. Note that any args passed to your script will be seen by the database-migration plugin's scripts since they use the same argsMap.
Here's an example script that just does the same thing as dbm-generate-changelog but adds a before and after message:
includeTargets << new File("$databaseMigrationPluginDir/scripts/DbmGenerateChangelog.groovy")
target(foo: "Just calls dbmGenerateChangelog") {
println 'before'
dbmGenerateChangelog()
println 'after'
}
setDefaultTarget foo
Note that I renamed the target from main to foo so it's unique, in case you want to call this from another script.
As an example of working with args, here's a modified version that specifies a default changelog name if none is provided:
println 'before'
if (!argsMap.params) {
argsMap.params = ['foo2.groovy']
}
dbmGenerateChangelog()
println 'after'
Edit: Here's a fuller example that captures the output of dbm-gorm-diff to a string:
includeTargets << new File("$databaseMigrationPluginDir/scripts/_DatabaseMigrationCommon.groovy")
target(foo: "foo") {
depends dbmInit
def configuredSchema = config.grails.plugin.databasemigration.schema
String argSchema = argsMap.schema
String effectiveSchema = argSchema ?: configuredSchema ?: defaultSchema
def realDatabase
boolean add = false // booleanArg('add')
String filename = null // argsList[0]
try {
printMessage "Starting $hyphenatedScriptName"
ByteArrayOutputStream baos = new ByteArrayOutputStream()
def baosOut = new PrintStream(baos)
ScriptUtils.executeAndWrite filename, add, dsName, { PrintStream out ->
MigrationUtils.executeInSession(dsName) {
realDatabase = MigrationUtils.getDatabase(effectiveSchema, dsName)
def gormDatabase = ScriptUtils.createGormDatabase(dataSourceSuffix, config, appCtx, realDatabase, effectiveSchema)
ScriptUtils.createAndPrintFixedDiff(gormDatabase, realDatabase, realDatabase, appCtx, diffTypes, baosOut)
}
}
String xml = new String(baos.toString('UTF-8'))
def ChangelogXml2Groovy = classLoader.loadClass('grails.plugin.databasemigration.ChangelogXml2Groovy')
String groovy = ChangelogXml2Groovy.convert(xml)
// do something with the groovy or xml here
printMessage "Finished $hyphenatedScriptName"
}
catch (e) {
ScriptUtils.printStackTrace e
exit 1
}
finally {
ScriptUtils.closeConnection realDatabase
}
}
setDefaultTarget foo