עצי ג’אווה
עצי ג’אווה הפכו לאופנה האחרונה באתרים אינטרנט ואפליקציות Web. הם זוכים לפופולאריות רבה בזכות הקלות בה ניתן להטמיע אותם לתוך מערכת קיימת, לבצע בהם שינויים ותחזוקה. בניגוד גמור לקלות ההתקנה והשימוש, ביצוע בדיקות אוטומטיות לעצי ג’אווה דינאמיים יכול להפוך לסיוט.
היום נבחן מקרוב עץ ג’אווה ייצוגי, שהפך למאוד פופולארי בפני עצמו (dTree), ושווריאציות שונות שלו צצות תחת כל עץ רענן, ומאחורי כל אתר קטן כגדול (ואפילו באתר פה!). במסגרת הטיפול בעץ נארוז הרבה קטעי קוד בפונקציות משלהם, עובדה שתאפשר לכם להשתמש בקוד שנדגים בקלות, ולהתאים אותו לצרכיכם.
האסטרטגיה לניווט בעץ היא ישירה למדי: בעוד שעצי ג’אווה רבים טוענים את כלל מבנה העץ מראש, QTP יכול לתפעל רק רמה אחת בכל שלב (אם אנחנו רוצים להישאר נאמנים לפעולות המשתמש) - ולכן, אנו נעבור ונרחיב רמה-רמה בעץ בהתאם למסלול הניווט. כל העבודה תעשה מאחורי הקלעים, ברמת אובייקטים ה-Run-Time, ובפועל המעטפת של QTP תשמש אותנו רק להצבעה על שורש העץ.
היות ואני אוהב להשתמש במחרוזות משורשרות, נבנה בדוגמה זו את מסלול הניווט כמחרוזת מהצורה Level1>Level2>Level3>…, כאשר כל רמה הינה שם הצומת בעץ. היות ויתכנו צמתים שונים בעלי אותו שם, באותה רמה, הניווט לא יוכל להתבצע באופן ליניארי (אנחנו עלולים להיכנס לתוך צומת שלא מכילה את שאר מסלול הניווט). עובדה זו תאלץ אותנו להבנות את הקוד באופן רקורסיבי, וקצת יותר מסובך מהרגיל.
זהו המקום להיזכר כיצד בנויים רוב עצי הג’אווה: בכל רמה, לצומת האב ישנם צמתים בנים, המתחלקים לשני סוגים: הילדים הישירים של האב, וצמתים המייצגים את הנכדים של האב, בהתאם לאחד הילדים. בפועל זה נראה כך :
צומת אב
בן1 (צומת בן אמיתי, ללא ילדים משלו)
בן2 (צומת בן אמיתי, עם שלושה ילדים משלו)
בן3 (צומת בן פיקטיבי, המחזיק בתוכו את שלושת הילדים של בן2)
בן4 (צומת בן אמיתי, עם ילד אחד משלו)
בן5 (צומת בן פיקטיבי, המחזיק בתוכו את הילד של בן4)
אבל כמובן שהמצב מסובך יותר ממה שתיארנו. לכל בן, יש "צמתי-עזר", הקיימים בלי קשר למספר הילדים שלו (או אם יש לו ילדים כלל). "צמתי-עזר" אלו כוללים רווחים לאינדנטציה, אייקון להרחבה/כיווץ, וכן את הטקסט (והקישור) עצמו של הצומת. כך שהתמונה האמיתית היא : היא :
צומת אב
…
בן2 (צומת בן אמיתי, עם שלושה ילדים משלו)
צמתי-עזר (רווחי אינדנטציה, אייקון להרחבה, והטקסט והקישור של בן2)
בן3 (צומת בן פיקטיבי, המחזיק בתוכו את שלושת הילדים של בן2)
…
בשורה התחתונה, שתי העובדות שחשוב לזכור הן:
א. כאשר אנחנו רוצים לבצע פעולה כלשהי בצומת (הרחבה, לחיצה להפעלת קישור וכו’), אנחנו בעצם רוצים לבצע פעולה על צמתי-העזר המצויים תחת הצומת הרלוונטי.
ב. כאשר אנחנו מדברים על הצמתים הכפופים לאב מסוים, יש לזכור כי צמתים אלו לא מצויים תחת צומת האב (שם מצויים צמתי-העזר), אלא הם מופיעים לצידו, כצומת אח פיקטיבי. צומת האב (שם מצויים צמתי-העזר), אלא הם מופיעים לצידו, כצומת אח פיקטיבי.
אנחנו עוד נראה איך עובדות אלו באות לכדי ביטוי בקוד הניווט בעץ.
הקוד המטפל בעץ נחלק לשלושה חלקים:
תחילה, עלינו למצוא את שורש העץ: לרוב זוהי משימה קלה – השורש הוא לרוב קישור בשם "בית" או "תיקיות" או טקסט דומה. לעומת זאת, ישנם אתרים בהם השורש הינו נטול טקסט, ומסתתר מאחורי הקלעים. הוא שם, כמובן, אבל קשר לאתר אותו באמצעות שימוש במאגר האובייקטים / : לרוב זוהי משימה קלה – השורש הוא לרוב קישור בשם "בית" או "תיקיות" או טקסט דומה. לעומת זאת, ישנם אתרים בהם השורש הינו נטול טקסט, ומסתתר מאחורי הקלעים. הוא שם, כמובן, אבל קשר לאתר אותו באמצעות שימוש במאגר האובייקטים / DP. דרך אחת להתגבר עלי הבעיה, היא להשתמש באחד מהצמתים העליונים בעץ, ולהתקדם ממנו במעלה ההיררכיה :
Function GetRoot(oQTPTopLevelNode)
Set GetRoot = oQTPTopLevelNode.Object.ParentNode.ParentNode
‘Notice that we climb twice up the hierarchy,
’since our line is actually a helper-node
End Function
Set GetRoot = oQTPTopLevelNode.Object.ParentNode.ParentNode
‘Notice that we climb twice up the hierarchy,
’since our line is actually a helper-node
End Function
במידה ולעץ שלכם יש שורש, ניתן לשנות את הפונקציה כך שתעלה רק רמה אחת במעלה ההיררכיה, או שתאתר את הצומת המחזיק את כל הבנים באופן אחר (הצומת עשוי להיות צומת-אח לשורש העץ). נסו ווריאציות שונות עד שתגיעו לתוצאה המתאימה לעץ שלכם.
כעת מגיע קוד הניווט עצמו::
פונקציית הניווט קוראת לפונקציות העזר הבאות :
IsNeededNode – בודקת האם מצאנו את הצומת שאנו צריכים לנווט אליו
ExpandNode – מרחיבה את הצומת הרלוונטית
ClickNode – מקליקה על קישור הטקסט של הצומת הרלוונטית
’sNavigate path = "level1>level2>level3>"
‘oParentNode = first it’s the root from the GetRoot function,
‘and later it will be recursively sent
Function NavigateTree(sNavigatePath, oParentNode)
Dim arrPath ‘Breaks down the path string
Dim i
Dim sNeededNode ‘Holds the current node to navigate to
Dim bNavigationSuccessful
Dim sNewNavigatePath ‘we’ll send this down the recursive chain
arrPath = Split(sNavigatePath, ">")
sNeededNode = arrPath(0)
‘Create a new navigation path without the current node
sNewNavigatePath = Replace(sNavigatePath, sNeededNode, "", 1, 1)
If Mid(sNewNavigatePath ,1,1 ) = ">" Then _
sNewNavigatePath = Mid(sNewNavigatePath ,2)
bNavigationSuccessful = False
‘Find the needed node
For i = 0 To oParentNode.Children.Length-1 ‘Loop through all the parent’s child-nodes
If IsNeededNode(sNeededNode, oParentNode.Children.Item(i)) Then
Call ExpandNode(oParentNode.Children.Item(i))
‘If we’re at the end of the navigation path, we should click
If sNewNavigatePath = "" Then
bNavigationSuccessful = ClickNode(oParentNode.Children.Item(i), sNeededNode)
Else ‘We should navigate - recursively call the function
‘Remember - the child nodes are held at the current node sibling, NOT under it
‘This is why the parent we send recursively is the current+1 child,
‘and not the current child
bNavigationSuccessful = NavigateTree(sNewNavigatePath, oParentNode.Children.Item(i+1))
End If
If bNavigationSuccessful = True Then Exit For ‘Navigation Completed
‘Notice that if the navigation went worng down the recursive chain,
‘we’ll continue searching other nodes with the same name
End If ‘Did we find the current node?
Next ‘Continue looping in search of the current node
NavigateTree = bNavigationSuccessful
End Function
‘oParentNode = first it’s the root from the GetRoot function,
‘and later it will be recursively sent
Function NavigateTree(sNavigatePath, oParentNode)
Dim arrPath ‘Breaks down the path string
Dim i
Dim sNeededNode ‘Holds the current node to navigate to
Dim bNavigationSuccessful
Dim sNewNavigatePath ‘we’ll send this down the recursive chain
arrPath = Split(sNavigatePath, ">")
sNeededNode = arrPath(0)
‘Create a new navigation path without the current node
sNewNavigatePath = Replace(sNavigatePath, sNeededNode, "", 1, 1)
If Mid(sNewNavigatePath ,1,1 ) = ">" Then _
sNewNavigatePath = Mid(sNewNavigatePath ,2)
bNavigationSuccessful = False
‘Find the needed node
For i = 0 To oParentNode.Children.Length-1 ‘Loop through all the parent’s child-nodes
If IsNeededNode(sNeededNode, oParentNode.Children.Item(i)) Then
Call ExpandNode(oParentNode.Children.Item(i))
‘If we’re at the end of the navigation path, we should click
If sNewNavigatePath = "" Then
bNavigationSuccessful = ClickNode(oParentNode.Children.Item(i), sNeededNode)
Else ‘We should navigate - recursively call the function
‘Remember - the child nodes are held at the current node sibling, NOT under it
‘This is why the parent we send recursively is the current+1 child,
‘and not the current child
bNavigationSuccessful = NavigateTree(sNewNavigatePath, oParentNode.Children.Item(i+1))
End If
If bNavigationSuccessful = True Then Exit For ‘Navigation Completed
‘Notice that if the navigation went worng down the recursive chain,
‘we’ll continue searching other nodes with the same name
End If ‘Did we find the current node?
Next ‘Continue looping in search of the current node
NavigateTree = bNavigationSuccessful
End Function
ואחרונות חביבות – פונקציות העזר:
‘Here you can add any custom behaivor relevant to you tree
‘For example, some tree cut the OuterText property after a certian length
‘So we should use another property or cut the expected sNodeName in the same manner
Function IsNeededNode(sNodeName, oNode)
If sNodeName = Trim(oNode.OuterText) Then
IsNeededNode = True
Else
IsNeededNode = False
End If
End Function
‘Here we search for the expand-icon helper node, under oNodeToExpaned
Sub ExpandNode(oNodeToExpaned)
Const sClosedNodeSearchString = "PLUS"
‘This tells us that the icon is + and not -
Const sPictureSearchString = "GIF"
‘This tells us the the helper node has an icon
‘ Change these constansts to suite you tree
Dim i
If oNodeToExpaned.Children.Length > 1 Then ‘If our parent node has helper nodes at all
For i = 0 to oNodeToExpaned.Children.Length - 1
‘Loop through helper nodes
If instr(uCase(oNodeToExpaned.Children.Item(i).InnerHTML), sPictureSearchString) > 0 Then
‘It’s an icon
If instr(uCase(oNodeToExpaned.Children.Item(i).InnerHTML), sClosedNodeSearchString) > 0 Then
‘Its’ a + icon
oNodeToExpaned.Children.Item(i).Click ‘Click the helper node, expending the tree
‘You can add sync or wait functions here, in order to wait for the tree to referesh
Exit For
End If ‘Found open-node image
End If ‘Found IMG node
Next
End If ‘The node has children
End Sub
‘Much like ExpandNode, but this time we search for
‘the text-link helper node
Function ClickNode(oNodeClick, sNodeName)
Dim i
For i = 0 to oNodeClick.Children.Length - 1 ‘Loop helper nodes
If sNodeName = oNodeClick.Children.Item(i).outerText Then
oNodeClick.Children.Item(i).Click
Exit For
End If
Next
ClickNode = True ‘Just for formality
End Function
‘For example, some tree cut the OuterText property after a certian length
‘So we should use another property or cut the expected sNodeName in the same manner
Function IsNeededNode(sNodeName, oNode)
If sNodeName = Trim(oNode.OuterText) Then
IsNeededNode = True
Else
IsNeededNode = False
End If
End Function
‘Here we search for the expand-icon helper node, under oNodeToExpaned
Sub ExpandNode(oNodeToExpaned)
Const sClosedNodeSearchString = "PLUS"
‘This tells us that the icon is + and not -
Const sPictureSearchString = "GIF"
‘This tells us the the helper node has an icon
‘ Change these constansts to suite you tree
Dim i
If oNodeToExpaned.Children.Length > 1 Then ‘If our parent node has helper nodes at all
For i = 0 to oNodeToExpaned.Children.Length - 1
‘Loop through helper nodes
If instr(uCase(oNodeToExpaned.Children.Item(i).InnerHTML), sPictureSearchString) > 0 Then
‘It’s an icon
If instr(uCase(oNodeToExpaned.Children.Item(i).InnerHTML), sClosedNodeSearchString) > 0 Then
‘Its’ a + icon
oNodeToExpaned.Children.Item(i).Click ‘Click the helper node, expending the tree
‘You can add sync or wait functions here, in order to wait for the tree to referesh
Exit For
End If ‘Found open-node image
End If ‘Found IMG node
Next
End If ‘The node has children
End Sub
‘Much like ExpandNode, but this time we search for
‘the text-link helper node
Function ClickNode(oNodeClick, sNodeName)
Dim i
For i = 0 to oNodeClick.Children.Length - 1 ‘Loop helper nodes
If sNodeName = oNodeClick.Children.Item(i).outerText Then
oNodeClick.Children.Item(i).Click
Exit For
End If
Next
ClickNode = True ‘Just for formality
End Function
שתלתי כמה צמתי דוגמה בעץ מאגר-הידע, על מנת שתוכלו להתנסות בקוד בעצמכם. העתיקו את קטעי הקוד של שלושת החלקים ל-QTP, והוסיפו בתחילתו את הקטע הבא (יתכן שסימני ה-> התהפכו, אם אתם לא מגיעים לתוצאות הרצויות, השתמשו בסימן ההפוך):
sNavigatePath = "מאגר הידע" & ">" & "טכניקות לסביבות ספציפ…" & ">" & "אינטרנט" & ">" & "עצי ג’אווה" & ">" & "דוגמה" & ">" & "דוגמת בת"
Set oQTPTree = Browser("title:=.*QTP.*", "index:=0").Page("title:=.*QTP.*", "index:=0").Link("html id:=spge1")
Set oRoot = GetRoot(oQTPTree)
msgbox NavigateTree(sNavigatePath, oRoot) ‘This does the actual navigation
Set oQTPTree = Browser("title:=.*QTP.*", "index:=0").Page("title:=.*QTP.*", "index:=0").Link("html id:=spge1")
Set oRoot = GetRoot(oQTPTree)
msgbox NavigateTree(sNavigatePath, oRoot) ‘This does the actual navigation
ניתן לראות כיצד פונקציית הניווט מתקנת את עצמה: היא מגיעה לצומת "דוגמה" הראשון, אולם לא מוצאת בו את צומת הבן "דוגמת בת". היא ממשיכה אוטומטית לצומת "דוגמה" השני, ומנווטת אליו. ניתן לראות זאת בבירור ע"י הורדת החלק האחרון ממסלול הניווט, וכפועל יוצא הפונקציה תנווט לצומת "דוגמה" הראשון, ותסתפק בו.
עצי הג’אווה בפרוייקט שלכם עשויים לדרוש שינויים והרחבות לקוד, אולם אני עדיין מקווה שהדוגמאות שנתתי יעזרו לכם להתמודד איתן בקלות. Having said that, כמובן שינם עצי ג’אווה בעלי מבנה פנימי שונים לחלוטין, ועבורם הקוד שהדגמתי לא רלוונטי לחלוטין.
שמור את המאמר לשימוש עתידי
הדפס את המאמר
A PDF Version Of This Post
ניווט במאגר הידע



January 16th, 2010 at 6:43 am
meridia :-( order levitra 842921 accutane asvwu viagra 753 cialis dgcjn
January 25th, 2010 at 11:09 pm
homeowners insurance :-)) car insurance quotes 8387 auto insurance eyp auto insurance rates 01157 long term car insurance 684
February 3rd, 2010 at 10:41 pm
health insurance curj health insurance 8-]]] life insurance >:-[ low car insurance rates :[[
February 7th, 2010 at 7:13 pm
buy tramadol :-[[[ aciphex lnuxz what is xanax 781 buy tramadol igupcm phentermine snz 2003 cialis levitra market sales viagra 50880 generic cialis kvaphq purchase ultram for pain rbqevl
February 10th, 2010 at 9:23 am
nj car insurance %-[[[ auto insurance njhe levitra asaw
February 11th, 2010 at 8:58 pm
online casino oqdani health insurance quotes 5962 health insurance =-[[ health insurance 26257 homeownersinsurance 474403
March 3rd, 2010 at 4:58 pm
snort taranabant :-[ ambien home page aeyquh zyrtec and pulsatile tinnitus iew prozac making me worse 43504 buy valium online 567969
March 4th, 2010 at 5:00 am
relief stress valium 440 weight loss diet desert burn hoodia :-DDD by inversine on line :-]]] can i drink and use celebrex 091980
April 13th, 2010 at 2:04 pm
using acetophenazine vmhf citalopram and pdr 319 cetirizine syrup jypbkb buy nimotuzumab at yvn
July 27th, 2010 at 11:38 am
618 levitra ppt 891 %-]]] hoodia west virginia 4254 radio control blackjack 55 >:-DDD buy paxil without a ska