I am using UFT One to test a Salesforce Contacts table with two rows that have clickable links, "Jim Bean" and "Marsha Smith". (see attached image).
I want to loop through the rows and click on the links and have "ValidateContactProperties" Action get called for each Contact Details page that comes up.
The following code works but ObjChildItem.Click() gets executed only the first time. Only Jim Bean's contact profile page is displayed, not Marsha Smith's.
For i = 2 to rowCount
Set ObjChildItem = obj(0).ChildItem(i,3,"Link", 0)
ObjChildItem.Click()
RunAction "ValidateContactProperties", oneIteration
Next
I can see ObjChildItem has Marsh Smith's URL and info but the page is still displaying Jim Bean's Contact Details page after ObjChildItem.Click() is executed for Marsha Smith.
How can we get Marsh Smith's Contact Details page to come after Jim Bean's?
****** WORKING CODE *********
I found a solution, it is not elegant but it works.
----------------- ---- Loop Through Contacts action -------------------
Set oDesc = Description.Create
oDesc("micclass").value = "WebTable"
Set obj = Browser("Contacts | Salesforce").Page("Contacts | Salesforce").ChildObjects(oDesc)
If obj is Nothing Then
Print "obj does not exist"
Else
' get the number of rows in the contacts table
rowCount = obj(0).GetROProperty("rows")
' global variable is initially set to 2
For i = gloVarIteration to rowCount
If gloVarIteration > 3 Then
' refresh the page if we are not in the first ieration of the loop, otherwise the DOM will gte messed up and UFT won't be able to recognize any objects.
Browser("Contacts | Salesforce").Refresh()
wait(5)
Set obj = Browser("Contacts | Salesforce").Page("Contacts | Salesforce").ChildObjects(oDesc)
End If
Set ObjChildItem = obj(0).ChildItem(i,3,"Link", 0)
If ObjChildItem is Nothing Then
Print "ObjChildItem does not exist"
Else
' bring up the Contact profile
ObjChildItem.Click()
' call the action to validate Contact profile data values
RunAction "ValidateContactProperties", oneIteration
End If
Next
End If
--------------- ValidateContactProperties action --------------------
If gloVarIteration > 2 Then
' refresh the page if we are not in the first ieration of the loop, otherwise the DOM will gte messed up and UFT won't be able to recognize any objects.
Browser("James Bean | Salesforce").Refresh()
End If
If Browser("James Bean | Salesforce").Page("James Bean | Salesforce").WebTabStrip("RelatedDetailsNewsMore").Exist(15) Then
....... do stuff
'increment global variable
gloVarIteration = gloVarIteration + 1
' go back to Contacts page
Browser("James Bean | Salesforce").Back()
End If
The problem you're facing is probably that the Click causes the browser to change the HTML DOM thus invalidating the objects in the Obj array. To understand why this happens please read this blog post.
In order to solve the issue you have to move the code that initializes Obj into the loop so there are valid objects for each loop iteration.
****** WORKING CODE *********
I found a solution, it is not elegant but it works.
----------------- ---- Loop Through Contacts action -------------------
Set oDesc = Description.Create
oDesc("micclass").value = "WebTable"
Set obj = Browser("Contacts | Salesforce").Page("Contacts | Salesforce").ChildObjects(oDesc)
If obj is Nothing Then
Print "obj does not exist"
Else
' get the number of rows in the contacts table
rowCount = obj(0).GetROProperty("rows")
' global variable is initially set to 2
For i = gloVarIteration to rowCount
If gloVarIteration > 3 Then
' refresh the page if we are not in the first ieration of the loop, otherwise the DOM will gte messed up and UFT won't be able to recognize any objects.
Browser("Contacts | Salesforce").Refresh()
wait(5)
Set obj = Browser("Contacts | Salesforce").Page("Contacts | Salesforce").ChildObjects(oDesc)
End If
Set ObjChildItem = obj(0).ChildItem(i,3,"Link", 0)
If ObjChildItem is Nothing Then
Print "ObjChildItem does not exist"
Else
' bring up the Contact profile
ObjChildItem.Click()
' call the action to validate Contact profile data values
RunAction "ValidateContactProperties", oneIteration
End If
Next
End If
--------------- ValidateContactProperties action --------------------
If gloVarIteration > 2 Then
' refresh the page if we are not in the first ieration of the loop, otherwise the DOM will gte messed up and UFT won't be able to recognize any objects.
Browser("James Bean | Salesforce").Refresh()
End If
If Browser("James Bean | Salesforce").Page("James Bean | Salesforce").WebTabStrip("RelatedDetailsNewsMore").Exist(15) Then
....... do stuff
'increment global variable
gloVarIteration = gloVarIteration + 1
' go back to Contacts page
Browser("James Bean | Salesforce").Back()
End If
Related
Given the following Kusto query:
range t from bin(now(), 1h)-23h to bin(now(), 1h) step 1h
| summarize t=make_list(t)
| project id='TS', val=dynamic([0,0,0,0,0,0,0,0,0,10,20,40,100,40,20,10,0,0,0,0,0,0,0,0]), t
| extend 5h_MovingAvg=series_fir(val, dynamic([1,1,1,1,1])),
5h_MovingAvg_centered=series_fir(val, dynamic([1,1,1,1,1]), true, true)
| render timechart
I am unable to get application insights to actually draw the moving average lines shown in this document
I have also tried applying the article to one of our actual applications and have not had any luck either. There are no errors or anything that would give a clue as to why the moving averages are not being drawn. I'm assuming there is a setting somewhere that most probably has to be set. Here is my custom query:
let timeGrain=1d;
let ago = ago(7d);
let mAvgParm = repeat(1, 5);
let dataset=requests
// additional filters can be applied here
| where timestamp >= ago and cloud_RoleName == "recalculateordercombination" and resultCode == 500
| where client_Type != "Browser" ;
// calculate failed request count for all requests
dataset
| make-series dailyFailure=sum(itemCount) default=0 on timestamp in range(ago, now(), timeGrain) by resultCode
// render result in a chart
| extend SMA = series_fir(dailyFailure, mAvgParm)
| render timechart
What are these queries missing in order to draw the moving average lines using series_fir?
ref articles used in my research
https://marckean.com/2019/03/25/log-analytics-advanced-queries/
https://learn.microsoft.com/en-us/azure/kusto/query/series-firfunction
https://learn.microsoft.com/en-us/azure/kusto/query/make-seriesoperator
The web clients for both services are different, and that's also true for their rendering logic.
In Azure Data Explorer (Kusto), you can just use render timechart on time-series data (which is typed as dynamic).
In other cases, you may need to first mv-expand the series (link to doc), before rendering it.
Here's an example which matches the first query in your question:
range t from bin(now(), 1h)-23h to bin(now(), 1h) step 1h
| summarize t=make_list(t)
| project id='TS', val=dynamic([0,0,0,0,0,0,0,0,0,10,20,40,100,40,20,10,0,0,0,0,0,0,0,0]), t
| extend 5h_MovingAvg=series_fir(val, dynamic([1,1,1,1,1])),
5h_MovingAvg_centered=series_fir(val, dynamic([1,1,1,1,1]), true, true)
| mv-expand val to typeof(long), t to typeof(datetime), 5h_MovingAvg to typeof(long), 5h_MovingAvg_centered to typeof(long)
| project t, 5h_MovingAvg, 5h_MovingAvg_centered, val
| render timechart
I would to make the field descriptions and label texts in my pages multi-lingual. Originally they are in English and I could let the user translate them through Google Translate. In order to avoid translation errors I would like to implement a translation data model that contains
FieldDisplayName / LabelText
FieldDisplayName_DE
FieldDisplayName_FR
FieldDisplayName_IT
etc.
All the pages contain a page header fragment that contains a menu button, searchbox etc. like in the Starter App template. I am planning on integrating a dropdown widget in the page header that allows to choose between the languages (DE,EN,FR,IT,...). Is it possible to bind the display name to the user's selection? How would I have to implement that?
The easiest way (to implement/use/maintain) that would provide highest possible translation quality will be introducing Translation data model with the following structure:
+----+--------+------------+------------+------------+-----+
| Id | Locale | FirstName | LastName | Age | ... |
+----+--------+------------+------------+------------+-----+
| 1 | EN | First name | Last name | Age | ... |
+----+--------+------------+------------+------------+-----+
| 2 | RU | Имя | Фамилия | Возраст | ... |
+----+--------+------------+------------+------------+-----+
| 3 | DE | Voornaam | Achternaam | Leeftijd | ... |
+----+--------+------------+------------+------------+-----+
| 4 | ... | ... | .... | ... | ... |
+----+--------+------------+------------+------------+-----+
In this model every column represents unique label within your app and every row represents labels's translations for supported languages. This model can be easily used in label bindings:
#datasources.UserTranslations.item.FieldNameToTranslate
Maintaining these translation will be easy as well, just drag and drop editable table on UI.
Here is a query script for the UserTranslations datasource:
// Assuming that you already have robust user settings implementation.
var userSettings = getUserSettings_();
var query = app.models.Translation.newQuery();
query.filters.Locale._equals = userSettings.Locale;
return query.run();
Radically different implementation will be
Introducing Calculated Model with the same set of fields as in the previous approach
Using Model Metadata API to extract display names from the model's fields
Translate fields using Translate API
Populate calculated model record with translated values
Here is super high level server pseudo script for that flow:
var userLocale = getUserLocaleFromUserSettings();
var fieldsDisplayNames = getFieldsDisplayNames(app.models.Translation);
var translations = translate(fieldsDisplayNames, 'en', userLocale);
var record = app.models.Translation.newRecord();
mapRecordFieldsToTranslations(record, translations);
return [record];
After some trials a translation model turned out to be too laggy for my demands. Therefore I have hardcoded the binding expression into the labels I want to translate. The binding expression looks a little bit like this:
(#pages.UserSettings.LanguageDropdown.value == 'EN') ? 'Contact' : 'Kontakt'
I have some data in Application Insights Analytics that has a dynamic object as a property of custom dimensions. For example:
| timestamp | name | customDimensions | etc |
|-------------------------|---------|----------------------------------|-----|
| 2017-09-11T19:56:20.000 | Spinner | { | ... |
MyCustomDimension: "hi"
Properties:
context: "ABC"
userMessage: "Some other"
}
Does that make sense? So a key/value pair inside of customDimensions.
I'm trying to bring up the context property to be a proper column in the results. So expected would be :
| timestamp | name | customDimensions | context| etc |
|-------------------------|---------|----------------------------------|--------|-----|
| 2017-09-11T19:56:20.000 | Spinner | { | ABC | ...
MyCustomDimension: "hi"
Properties:
context: "ABC"
userMessage: "Some other"
}
I've tried this:
customEvents | where name == "Spinner" | extend Context = customDimensions.Properties["context"]
and this:
customEvents | where name == "Spinner" | extend Context = customDimensions.Properties.context
but neither seem to work. They give me a column at the end named "Context" but the column is empty - no values.
Any ideas?
EDIT:
Added a picture for clarifying the format of the data:
edited to working answer:
customEvents
| where name == "Spinner"
| extend Properties = todynamic(tostring(customDimensions.Properties))
| extend Context = Properties.context
you need an extra tostring and todynamic in here to get what you expect (and what i expected!)
the explanation i was given:
Dynamic field "promises" you the upper/outer level of key / value access (this is how you access customDimensions.Properties).
Accessing internal structure of that json depends on the exact format of customDimensions.Properties content. It doesn’t have to be json by itself. Even if it looks like a well structured json, it still may be just a string that is not exactly well formatted json.
So basically, it by default won't attempt to parse strings inside of a dynamic/json block because they don't want to spend a lot of time possibly trying and failing to convert nested content to json infinitely.
I still think that extra tostring shouldn't be required inside there, since todynamic should already be allowing both string and dynamic in validly, so i'm checking to see if the team that owns the query stuff can make that step better.
Thanks sooo much.. just to expand on the answer from John. We needed to graph duration of end-points using custom events. This query made it so we could specify the duration as our Y-axis in the chart:
customEvents
| extend Properties = todynamic(tostring(customDimensions.Properties))
| extend duration = todouble(todecimal(Properties.duration))
| project timestamp, name, duration
I have a form with a record number, then boxes for "primary" notes and effective date.
Then I have an optional "secondary" notes and effective date box.
When someone clicks on a button to edit the primary or secondary notes, it looks to see if the other notes have changed (a session variable set to "true" fires for the "textchanged" event for the other textbox). If so, instead of enabling the textbox that matched the "edit" button, it flashes a message saying to cancel or update the other textbox's changes.
My problem is that the contents of the textbox are being viewed as "changed" when nothing is changed. If I load the record, and run a separate validation check, it verifies that the contents of the textbox match with the data originally loaded into the textbox. Then, not changing anything, when I click on the "edit" button, that separate validation comes back as no longer matching, even though the information in the session variable and the textbox are both unchanged.
Here is my validation check -
If Not Session("SN1") Is Nothing Then
sesslst = sesslst & "<p>Sec Notes match = " & (Trim(Session.Item("SN1").ToString) = Trim(txtSecondaryNotes.Text)).ToString
sesslst = sesslst & "<p>Sec Date match = " & (Trim(Session.Item("SD1").ToString) = Trim(txtSecEffDate.Text)).ToString
sesslst = sesslst & "<p>S = " & Trim(Session.Item("SN1").ToString)
sesslst = sesslst & "<p>F = " & Trim(txtSecondaryNotes.Text)
End If
lblTest.Text = sesslst
Here are the results after initial record load:
SN1 - Bill Smith (Father) 10/12/1971 Linda Smith (Mother) 7/22/1971 Boudreau W Smith (Brother) 6/7/1994
SD1 - 4/1/2014 12:00:00 AM
Sec Notes match = True
Sec Date match = True
S = Bill Smith (Father) 10/12/1971 Linda Smith (Mother) 7/22/1971
Boudreau W Smith (Brother) 6/7/1994
F = Bill Smith (Father) 10/12/1971 Linda Smith (Mother) 7/22/1971
Boudreau W Smith (Brother) 6/7/1994
(all specific data in this example has been changed, so no real personal information is being displayed)
When I click the edit button and that validation loop runs again, the "Sec Notes changed" value changes to "false," but nothing else changes.
Initially, I had the session variable for the record change set for the "TextChanged" event. When it kept setting, despite no change, I added additional validation within the change event. If both the notes and dates session variable do not match the field information, it sets another session variable as changed -
Protected Sub txtSecondaryNotes_TextChanged(sender As Object, e As EventArgs) Handles txtSecondaryNotes.TextChanged, txtSecEffDate.TextChanged
If Not ((Trim(Session.Item("SN1").ToString) = Trim(txtSecondaryNotes.Text)) And (Trim(Session.Item("SD1").ToString) = Trim(txtSecEffDate.Text))) Then
Session.Item("secondaryEditing") = True
End If
End Sub
I never get passed the check for the "secondaryEditing" session variable when I click the edit button (the lstSess() proc just lists all the session variable names and values so I can see what's being compared) -
Protected Sub btnEditPrimary_Click(sender As Object, e As EventArgs) Handles btnEditPrimary.Click
txtPolicy.Text = Trim(txtPolicy.Text).ToUpper
lstSess()
If Session.Item("secondaryEditing") = True Then
secMsg.Visible = True
Exit Sub
Else
secMsg.Visible = False
End If
So, I thought the problem, initially, was maybe with the form, loading of data, and the TextChanged event firing but not being evaluated until the button was pushed, but even when I add in the additional If-->then validation within the event, it still thinks something has changed, when the data in the field has not even been enabled, let alone manipulated.
How can I get this to honestly evaluate whether the text in the field has changed? FYI - the asp:TextBox objects have been set with and without "AutoPostBack = 'true'" with no difference in how it runs.
Save your efforts, folks. The original programmer did not import all of the necessary System and System.Web classes.
I don't know if the follow code snippet intend to work in this way, because sometimes we "as developers" try automate creation of data display control where number of fields are uncontrolled and with similar data-binding, so before I review the application some guys left this :
Under ActiveReport_ReportStart() event :
for (Ind = 1; Ind <=CM.Length; Ind++) {
if (Ind == 1) {
Left = ((Line)rpt.Sections["PageHeader"].Controls["lnH8"]).Left + 0.05f;
} else if (Ind == 2) {
Left = ((Line)rpt.Sections["PageHeader"].Controls["lnH9"]).Left + 0.05f;
} else if (Ind == 3) {
Left = ((Line)rpt.Sections["PageHeader"].Controls["lnH10"]).Left + 0.05f;
}
TextBox TB = new TextBox();
TB.Size = ((Label)rpt.Sections["PageHeader"].Controls["tbColorway1"]).Size;
TB.Font = ((Label)rpt.Sections["PageHeader"].Controls["tbColorway1"]).Font;
TB.Width = ((Label)rpt.Sections["PageHeader"].Controls["tbColorway1"]).Width;
TB.Height = ((Label)rpt.Sections["PageHeader"].Controls["tbColorway1"]).Height;
TB.VerticalAlignment = VerticalTextAlignment.Top;
TB.Location = new System.Drawing.PointF(Left, ((Label)rpt.Sections["PageHeader"].Controls["tbColorway1"]).Top);
TB.DataField = "ColorText" + Ind + ColorwayNumber;
rpt.Sections["Detail"].Controls.Add(TB);
It doesn't have compilation error when is previewed, also others fields that are not auto-generated are displayed correctly (ReporHeader, ReportFooter), but IMHO I think is better to replace this mechanism by a subreport inside the detail section, of course these fields have to be displayed in Detail section of the report. Anyways I would like to see some recommendations because if is possible to auto-generated textbox or labels in runtime I will have to explain to boss why this code was not working, and if I have to use subreports instead, I need to know how to pass parameter (at least I need to work with two parameters for generate another sql query for it) and what "event" is proper to put script into it..
After I discovered that most important problem is to get report format changes by how many field/textbox were added to report detail in runtime restricted by a sqlquery return value, for example :
Returned SQLQuery value = 4
10 fields generated for detailed row 1
6 fields for row 2
4 fields for row 3
Detail fields are bound to a SQL Store Procedure*
Report will supossed to be printed/showed in this way :
//Report Init
Page 1 :
|field 1|field 2|field 3|field 4|
------------------------------------------------------
row1 | valA1 | valA2 | valA3 | valA4 |
------------------------------------------------------
row2 | valB1 | valB2 | valB3 | valB4 |
------------------------------------------------------
row3 | valC1 | valC2 | valC3 | valC4 |
------------------------------------------------------
Page 2 :
|field 1|field 2|field 3|field 4|
------------------------------------------------------
row1 | valA5 | valA6 | valA7 | valA8 |
------------------------------------------------------
row2 | valB5 | valB6 |
------------------------------------------------------
row3
------------------------------------------------------
Page 3 :
|field 1|field 2|field 3|field 4|
------------------------------------------------------
row1 | valA9 | valA10|
------------------------------------------------------
row2
------------------------------------------------------
row3
------------------------------------------------------
//End of Report
Any help will be appreciated
Thank you so much
It is perfectly fine to dynamically create fields on a report at runtime. The creation of those fields do indeed need to be done in the reportstart event or earlier (i.e. before calling ActiveReport.Run).
However, you could place the same logic to dynamically create those fields in a subreport and pass a parameter too, but in general subreports do impose additional overhead (and an additional query in most cases) so I wouldn't use a subreport unless there is a compelling benefit. However, there is a walkthrough on passing parameters to a subreport here.
The only thing that looks suspect in your code is the following line:
TB.Location = new System.Drawing.PointF(Left, ((Label)rpt.Sections["PageHeader"].Controls["tbColorway1"]).Top);
You are using the Top value from a control in the PageHeader, but TB is in the Detail section. I can understand reusing the Left value, but reusing the Top value wouldn't be consistent across different sections (Top is it's vertical position from the top of the section containing the control)
Now, it sounds like sometimes these fields do not appear on the report. Some things you can verify to troubleshoot the problem:
Determine if there is a binding problem or a visual/location problem. TO do this, just give the textbox a border or a background color or something so you can see it even if there is no text (due to failed data binding).
Start logging out the position of each textbox and the datafield value to a log file. When you notice the problem, go back to the log and see if you can identify what triggers the problem (maybe a specific index, location or datafield value?).
Finally, make sure that the page size (determined by the system's default printer) is not changing and maybe cutting off one of your dynamically added textboxes.