I am using Concordion to test some java components.
I will write something like
<pre concordion:execute="someFunction(#TEXT, #a)">
{
id:123,
name:<span concordion:echo="#b"/>
}
</pre>
I want to value of #b is calculated and substituted dynamically.
But instead of value #b in someFunction comes an empty string.
Similarly, if use
name:<span concordion:execute="getBValue()"/>
If someone has done something like this, could you please help.
Thanks.
I think Concordion was not design for the purpose of this use case. It is not a template engine. You can transfer the text elements of your specification as input into your automated tests. Additionally, you can use the values inside your specifications as reference to compare the actual outputs of the system under test.
When you want to transfer the result of some method getBValue() into another method someFunction() you have probably several option:
What about calling getBValue() inside your someFunction()?
Or you could transfer the result of getBValue() into a variable and call some Function with this variable:
<pre concordion:execute="someFunction(#TEXT, #a, #b)">
{
id:123,
name:placeholder-for-value-b
}
</pre>
Then inside of someFunction() you could replace the placeholder:
public void someFunction(String text, String aValue, String bValue) {
text.replace("placeholder-for-value-b", bValue);
//continue logic of someFunction
}
you could use the execute command to initialize the variable #name:
<span concordion:execute="#name=getBValue()"/>
followed by the echo command:
name: <span concordion:echo="name" />
or have you tried to call your method directly within the echo command?
name: <span concordion:echo="getBValue()" />
Could your problem be related with the html structure?
When you are using nested elements such as
<pre concordion:execute=...> <span concordion:assert-equals=...>...</span> <span concordion:set=...>...</span> </pre>
Concordion uses the following execution order:
all "standard" commands such as set, echo, etc.
the execute command
assert commands such as assert-equals
This is the way Concordion handles unusual sentence structures.
What about putting the execute-command inside a dedicated span-tag?
<pre> <span concordion:execute=...>...</span> <span concordion:echo=...></span> </pre>
Related
I'm trying to pass the value from a C# object to a JavaScript function.
But I am getting an error:
#foreach (var Item in Model.Student)
{
<input id="btnAdd" type="button" value="Add" onclick="AddTextBox(JSON.parse(#Html.Raw(Item)))"/>
}
The Model.Student property returns a Student object, which is defined as follows:
public class Student
{
public int Id { get; set; }
public string name { get; set; }
}
You need to do three things to resolve this issue:
Serialize your Student object (Item) to JSON using Json.Serialize(),
Encode your JSON as a JavaScript string using HttpUtility.JavaScriptStringEncode(), and
Wrap your JSON in apostrophes so that JSON.parse() is correctly interpreting it as a string.
Your final Razor markup should look like the following:
<input id="btnAdd" type="button" value="Add" onclick="AddTextBox(JSON.parse('#Html.Raw(HttpUtility.JavaScriptStringEncode(Json.Serialize(Item).ToString()))'))"/>
Continue reading for a deeper understanding of how to troubleshoot these types of issues in the future, as well as why each one of these steps is necessary.
Debugging
Before diving in, it's worth taking a step back to revisit some troubleshooting principles. Remember that ASP.NET Core MVC is simply rendering an HTML page. After that, it's up to the browser to parse and execute both the HTML and the JavaScript. When you're combining Razor, HTML, and JavaScript in the same line, but only looking at the errors generated by JavaScript, it's easy to miss the underlying issue. Given this, what you want to do is view the source code in your browser to see what HTML is being generated.
Issue #1: Serializing Classes
The most immediate issue is that when you call #Html.Raw(Item), the Item's ToString() method is going to be called to create a string representation of your Student object. If your Student object is a class, this will just be the fully-qualified name of the class (e.g., MyNamespace.Student).
This will output something like:
<input id="btnAdd" type="button" value="Add" onclick="AddTextBox(JSON.parse(MyNamespace.Student))"/>
That's obviously not valid JSON, nor does it include any of your data. To resolve this, you need to wrap the call to Item in Json.Serialize(), which will serialize it from a C# object to a JSON string:
<input id="btnAdd" type="button" value="Add" onclick="AddTextBox(JSON.parse(#Html.Raw(Json.Serialize(Item))))"/>
This will output something closer to what you want:
<input id="btnAdd" type="button" value="Add" onclick="AddTextBox(JSON.parse({"Id"=1,"name":"John"}))"/>
Issue #2: Encoding JSON
If you attempt to execute the above code, you're going to encounter a JavaScript error, such as the following:
Uncaught SyntaxError: Unexpected end of input
Do you see the problem? The quote around Id is going to close your onclick handler, resulting in malformed markup. From the browser's perspective, it ends up seeing the following attributes, trailed by invalid markup:
id: btnAdd
type: button
value: Add
onclick: AddTextBox(JSON.parse('{
To remedy this, you need to encode the JSON string so that it can be embedded within a JavaScript call. Fortunately, ASP.NET Core supplies us with the HttpUtility.JavaScriptStringEncode() method that does just that:
<input id="btnAdd" type="button" value="Add" onclick="AddTextBox(JSON.parse(#Html.Raw(HttpUtility.JavaScriptStringEncode(Json.Serialize(Item).ToString()))))"/>
Note: The JavaScriptStringEncode() method requires a string, so we need to call ToString() on the Json.Serialize() call, which otherwise outputs an IHtmlContent object.
This will now output something like the following:
<input id="btnAdd" type="button" value="Add" onclick="AddTextBox(JSON.parse({\"Id\"=1,\"name\":\"John\"}))"/>
Issue #3: Converting to a JSON object
There are still problems here. The JSON.parse() method expects a string, but we're passing it raw JSON notation. As such, if you execute this code, you'll now receive a variation on the original error:
Uncaught SyntaxError: Invalid or unexpected token
This can be resolved by simply wrapping the JSON in apostrophes, denoting that it's a string:
<input id="btnAdd" type="button" value="Add" onclick="AddTextBox(JSON.parse('#Html.Raw(HttpUtility.JavaScriptStringEncode(Json.Serialize(Item).ToString()))'))"/>
This will now output something like the following:
<input id="btnAdd" type="button" value="Add" onclick="AddTextBox(JSON.parse('{\"Id\"=1,\"name\":\"John\"}'))"/>
If you click on this element, this should finally workâassuming, of course, your AddTextBox() is functioning correctly.
Conclusion
As mentioned at the top, when working with Razor, HTML, and JavaScript, you really need to pay close attention to what HTML is being rendered from the Razor page, prior to it being parsed by the browser or executed by the JavaScript engine. In this case, most of these issues stem from malformed markup that either the browser or the JavaScript engine aren't able to properly parse.
I'm sorry if this is already solved but I'm not able to find it so I gonna try to be quick.
Imagine one of the props received by my component has the following value:
myAnnoyingProp: "Position of {{$data.name}} {{maritalStatus?\'married\':\'free\'}}"
I tried the following two options but I've got the same result for both:
<label v-html="element.label"></label>
<label>
{{element.label}}
</label>
Expected result:
Position of TheNameSetted free
Obtained result:
Position of {{$data.name}} {{maritalStatus?\'married\':\'free\'}}
PD: I'm using Vue 2.4.2
You could move the login inside your component by making use of 2 props, for example name, which is a String, and maritalStatus, which is either a String or Boolean depending on your needs (the example assumes a Boolean for maritalStatus). Then inside your component construct the message you want to display:
<label>
Position of {{element.name}} <span v-if="maritalStatus">Free</span><span v-else>Married</span>
</label>
You could also use string literals:
myAnnoyingProp: `Position of ${name} ${maritalStatus}`
I have a custom component that conditionally renders either a link to path or a span for disabled link if the supplied path-disabled method determines so, the internals of which are unimportant other than it works when used like this:
<li>
<conditional-link path="/step/1" :path-disabled="pathDisabled">
<span class="number">1</span>
Step one
</conditional-link>
</li>
But if I do this it fails:
<li v-for="route in stepPaths['/step'].subRoutes">
<conditional-link path="{{route.fullPath}}" :path-disabled="pathDisabled">
<span class="number">{{route.number}}</span>
{{route.title}}
this outputs correct path:
{{route.fullPath}}
</conditional-link>
</li>
The path property value is the litteral string {{route.fullPath}}.
I tried path="route.fullPath" but then the path is the litteral string route.fullPath.
How do I get the path value into the path property in a loop? The variable is correct as it renders fine within the inside of the component.
OK it was easy so in case any other newbies run into this, you have to bind the object in the v-for to be able to use the object directly:
<li v-for="route in stepPaths" :route="route">
<conditional-link :path="route.fullPath" ...
I've got a very simple function, of replacing the innerHTML of a element. I've been trying to debug this for hours but simply can't, and it's infuriating.
When called from a button press the JavaScript (as follows) works well, but when called from another function it doesn't work. I am totally lost as to why this might be, and its a fairly core part of my app
// This loaded function in my actual code is a document listener
// checking for when Cordova is loaded which then calls the loaded function
loaded();
function loaded() {
alert("loaded");
changeText();
}
function changeText() {
alert("started");
document.getElementById('boldStuff').innerHTML = 'Fred Flinstone';
}
Button press and HTML to replace
<div id="main">
<input type='button' onclick='changeText()' value='Change Text'/>
<p>Change this text >> <b id='boldStuff'> THIS TEXT</b> </p>
</div>
It is also here in full on JSFiddle
You are already changed the innerHTML by calling the function loaded(); on onLoad.
Put this in an empty file and same as .html and open with browser and try. I have commented the function loaded();. Now it will be changed in onclick.
<div id="main">
<input type='button' onclick='changeText();' value='Change Text'/>
<p>Change this text >> <b id='boldStuff'> THIS TEXT</b> </p>
</div>
<script>
//loaded();
function loaded() {
alert("loaded");
changeText();
}
function changeText() {
alert("started");
document.getElementById('boldStuff').innerHTML = 'Fred Flinstone';
}
</script>
The problem here is, that the element you're trying to manipulate is not yet existing when you are calling the changeText() function.
To ensure that the code is only executed after the page has finished loading (and all elements are in place) you can use the onload handler on the body element like this:
<body onload="loaded();">
Additionally you should know, that it's very bad practice to manipulate values by using the innerHTML property. The correct way is to use DOM Manipulations, maybe this can help you.
You script loads before the element (boldStuff) is loaded,
Test Link - 1 - Put the js in a seperate file
Test Link - 2 - put the js at the very end, before closing the <body>
I'm using Castles' NVelocity Engine to do some template work. Here's the problem. Several of my templates work fine, but one of them isn't.
#foreach($i in $Items)
<div class="grid_3 folioItem"> <a rel="prettyPhoto[portfolio]" href="$i.Link" class="lightBox"><img src="$i.Image" width="220" height="125" alt="showcase" /></a>
<h4>$i.ShortName</h4>
<p>$i.LongName</p>
<p><a class="button pngFix" href="$i.Link">$i.LinkText</a></p>
</div>
#end
For some reason, the above code works half way. I get six sets of the div tags with all the innards, but Velocity outputs $i.ShortName instead of the contents on $i.ShortName. Any clue why this is? If I get six outputs that would leave me to believe that Items is set up correctly and exists in the Velocity Template. But for some odd reason it's children don't.
Now Items is a List<CategoryItem> and I've checked over and over again to make sure that I haven't misspelled the names of the members.
What am I missing?
Okay. So I figured it out (I think) it seems to be that sub objects will only expose their properties to the template. For instance:
public class Item{
public string BadName;
public stirng GoodName {
get {
return "Foo"
}
}
}
GoodName can be referenced in the template, But BadName cannot