IntelliJ GDSL jenkinsfile nested properties not working - intellij-idea

contributor(ctx) {
method(name: 'pipeline', type: Object, params: [body: Closure], doc: 'Declarative pipeline')
property(name: 'env', type: 'org.jenkinsci.plugins.workflow.cps.EnvActionImpl.Binder', doc: 'Environment variable')
}
def envVars = context(ctype: 'org.jenkinsci.plugins.workflow.cps.EnvActionImpl.Binder')
contributor(envVars) {
property(name: 'GIT_BRANCH', type: String, doc: 'Branch name')
}
With the example above, after plenty of googling and examples, this should be the way it is written and seems to be in all the examples.
However, in the Jenkinsfile env.GIT_BRANCH env is recognized, but for the other half, it's No candidates found for method call env.GIT_BRANCH.
Same for everything that uses property.property style.
Is this a known issue/limitation (could not find it mentioned anywhere)?

Related

IntelliJ GSDL: Define method with optional arguments

I have Jenkins pipeline shared library, that specifies a global variable foo which provides two methods.
One of the has no argument, the other has one optional argument:
/vars/foo.groovy
def getBarOne() {
//...
}
def getBarTwo(String value = '') {
//...
}
Now I want to provide an IntellJ GSDL file which supports useful code completion for both of this methods.
(The GSDL provided by my Jenkins only contains only the definition for the global variable but not for its methods, so I'm trying to add that.)
pipeline.gsdl (by Jenkins)
//The global script scope
def ctx = context(scope: scriptScope())
contributor(ctx) {
//...
property(name: 'foo', type: 'org.jenkinsci.plugins.workflow.cps.global.UserDefinedGlobalVariable')
}
//..
pipeline.gsdl (pimped by me)
//The global script scope
def ctx = context(scope: scriptScope())
contributor(ctx) {
//...
property(name: 'foo', type: 'org.jenkinsci.plugins.workflow.cps.global.UserDefinedGlobalVariable')
}
def uservarCtx = context(ctype: 'org.jenkinsci.plugins.workflow.cps.global.UserDefinedGlobalVariable')
contributor (uservarCtx) {
method name: 'getBarOne', type: 'java.lang.String', params: [:]
method name: 'getBarTwo', params: [value:'java.lang.String'], type: 'List<String>'
}
//..
So far so good.
However, code completion in my Jenkinsfile is not fully satisfactory, as it suggests
For getBarOne() it suggests both .barOne and .getBarOne(); for getBarTwo(..) only .getBarTwo(String value) is suggested, although the argument is optional.
How can I specify in the GDSL file, that the argument is optional, so that I get suggested all three (valid groovy) options: barTwo, getBarTwo() and getBarTwo(String value)?
(Unfortunately the “GDSL AWESOMENESS” Series was of no help.)
To have all three options provided, one has to specify two method signatures in the GDSL file.
One with the (optional) argument, an one without it:
pipeline.gdsl
//...
def uservarCtx = context(ctype: 'org.jenkinsci.plugins.workflow.cps.global.UserDefinedGlobalVariable')
contributor (uservarCtx) {
method name: 'getBarOne', type: 'java.lang.String', params: [:]
method name: 'getBarTwo', params: [:], type: 'List<String>' //<---
method name: 'getBarTwo', params: [value:'java.lang.String'], type: 'List<String>'
}
autocomplete suggestions:
Bonus track: Multiple global variables
As I've not only one global variable but two, I also wanted to have auto-completion to support that as well.
The trick to do that is, to have specifiy different types for your global variables:
pipeline.gsdl
//The global script scope
def ctx = context(scope: scriptScope())
contributor(ctx) {
//...
property(name: 'foo', type: 'org.jenkinsci.plugins.workflow.cps.global.UserDefinedGlobalVariable.Foo')
property(name: 'bar', type: 'org.jenkinsci.plugins.workflow.cps.global.UserDefinedGlobalVariable.Bar')
}
def varCtxFoo = context(ctype: 'org.jenkinsci.plugins.workflow.cps.global.UserDefinedGlobalVariable.Foo')
contributor (varCtxFoo) {
//...
}
def varCtxBar = context(ctype: 'org.jenkinsci.plugins.workflow.cps.global.UserDefinedGlobalVariable.Bar')
contributor (varCtxBar) {
//...
}
//..
Note the .Foo and .Bar suffix to the type UserDefinedGlobalVariable with the type definitions.

Polymer: Call a behavior's property function in elements property values?

Recently I looked up Polymers app-localize-behavior and I saw they typed the localize() method as Function (see on GitHub):
excerpt from app-localize-behavior.html:
localize: {
type: Function,
computed: '__computeLocalize(language, resources, formats)'
},
This method works perfectly fine in data-bindings, like <div>{{localize('welcome')}}</div>, but how can I call this method from my elements properties? I try to do something like:
excerpt from my-element.html:
properties: {
_pageTitle: {
type: String,
value: this.localize('welcome')
}
}
But when I try this, I get a Uncaught TypeError: this.localize is not a function. Even in my ready method I need to call this.localize asynchronously as otherwise it isn't defined, too.
How could I solve that problem?
Thank you in advance!
Use a computed property that invokes localize(...):
properties: {
_pageTitle: {
computed: 'localize("welcome")'
}
}
demo

How jasmine spy example works

All;
I am just starting learning Jasmine( version 2.0.3 ), when I got to Spies section, the first example confused me:
describe("A spy", function() {
var foo, bar = null;
beforeEach(function() {
foo = {
setBar: function(value) {
bar = value;
}
};
spyOn(foo, 'setBar');
foo.setBar(123);
foo.setBar(456, 'another param');
});
it("tracks that the spy was called", function() {
expect(foo.setBar).toHaveBeenCalled();
});
it("tracks all the arguments of its calls", function() {
expect(foo.setBar).toHaveBeenCalledWith(123);
expect(foo.setBar).toHaveBeenCalledWith(456, 'another param');
});
it("stops all execution on a function", function() {
expect(bar).toBeNull();
});
});
I wonder if anyone could explain why the setBar function does not affect the bar defined inside describe block? How Jasmine spies deal with this?
Thanks
Because you are not actually executing the methods.
If you want this test to fail:
it("stops all execution on a function", function() {
expect(bar).toBeNull();
});
After these calls:
foo.setBar(123);
foo.setBar(456, 'another param');
Then you should call and.callThrough for your spy.
spyOn(foo, 'setBar').and.callThrough();
From the documentation
Spies: and.callThrough
By chaining the spy with and.callThrough, the spy will still track all
calls to it but in addition it will delegate to the actual
implementation.
With regard to your question, 'how jasmine deals with this?'
From here you can read a basic explanation:
Mocks work by implementing the proxy pattern. When you create a mock
object, it creates a proxy object that takes the place of the real
object. We can then define what methods are called and their returned
values from within our test method. Mocks can then be utilized to
retrieve run-time statistics on the spied function such as:
How many times the spied function was called.
What was the value that the function returned to the caller.
How many parameters the function was called with.
If you want all of the implementation details, you can check the Jasmine source code which is Open Source :)
In this source file CallTracker you can see how the gather data about the method calls.
A little more about the proxy pattern.

Dojo this.inherit throws 'Uncaught TypeError: Cannot read property 'callee' of undefined'

To facilitate a JsonRest store with a non-standard url scheme, I am trying to inherit JsonRest and override the _getTarget(id) function. Here is what my inherited javascript class looks like:
define([
"dojo/_base/declare",
"dojo/store/JsonRest",
],
function(declare, JsonRest) {
return declare(JsonRest, {
_getTarget: function(id){
var target = this.target;
if(typeof id != "undefined"){
if(target.indexOf("{id}") != -1) {
//use template
target = target.replace("{id}", id);
} else {
target = this.inherited(id);
}
}
return target;
},
});
});
However the line target = this.inherited(id); returns an error: Uncaught TypeError: Cannot read property 'callee' of undefined.
I looked at the docs, and I think I am doing it right:
http://dojotoolkit.org/reference-guide/1.10/dojo/_base/declare.html#calling-superclass-methods
What is the proper way to call the base class's _getTarget(id) function?
If you look closely the part of the documentation you linked, you are supposed to literally pass the arguments object to this.inherited - that's what contains the callee property it is looking for (and will include the id and any other arguments anyway, to be passed along to the superclass).
A few paragraphs in, the documentation also explains how to call this.inherited with arguments other than the same ones passed, if necessary: you can pass custom arguments in an array after arguments, i.e. this.inherited(arguments, [ ... ]). arguments always has to be first.

How do I check if Geb Module "content" is present?

I'm a bit new to the whole Selenium/Geb thing, so I'm probably going about this a bit wrong, but I'm trying to get the exists() method in the following code to work properly.
class Question extends Module {
static base = { $("fieldset") }
static content = {
input { $("input[type=text]") }
name { input.getAttribute("name").toString() }
}
boolean exists() {
return input.isPresent()
}
Frustratingly, when I try to execute that code (from a Spock Test, "go"ing to a PageObjectm including this module, I get the following:
The required page content 'input - SimplePageContent (owner: question - Question (owner: MyPage, args: [], value: null), args: [], value: null)' is not present
I've tried a number of other things, including:
if (input) return true; return false,
... input.size() == 0,
Using static at = {...} (doesn't seem to be supported for modules"
Any ideas
By default Geb ensures that your content definitions return non empty elements. If your content is optional you can tell it using required content option:
name(required: false) { input.getAttribute("name").toString() }
Because Geb utilizes Groovy Truth to redefine how navigator are coerced to boolean values(empty navigators are falsey and non-empty are truthy) you can simplify your exists method to:
boolean exists() {
input
}