I'm new with robotfw.
I would like to get current URL and edit it and then reuse part of it
like this:
Navigate to somewhere
${Url} = Get location
#when ${Url} is www.site.com/123/wrongplace
#${Url2} = www.site.com/123/
click link link=${Url2}
Need only www.site.com/123 part of it. But the
number part '123' changes everytime so first slash from right would be the marker to use..
How to edit it?
Thanks in advance.
You can try to use the Split String From Right keyword.
Here is an example to help you:
*** Settings ***
Library String
*** Test Case ***
mytest
${url} = Set Variable www.site.com/123/wrongplace
${modified_url} = remove_chars_after_first_slash_from_right ${url}
Log To Console \n${url} changed into ${modified_url}
${url} = Set Variable www.whatever.com/63466/to_remove
${modified_url} = remove_chars_after_first_slash_from_right ${url}
Log To Console \n${url} changed into ${modified_url}
*** Keywords ***
remove_chars_after_first_slash_from_right
[Arguments] ${string}
${begin} ${end} = Split String From Right ${string} / 1
[return] ${begin}
And here is the output of this test:
$ robot test.robot
==============================================================================
Test
==============================================================================
mytest ..
www.site.com/123/wrongplace changed into www.site.com/123
www.whatever.com/63466/to_remove changed into www.whatever.com/63466
mytest | PASS |
------------------------------------------------------------------------------
Test | PASS |
1 critical test, 1 passed, 0 failed
1 test total, 1 passed, 0 failed
==============================================================================
Hey thanks for your example! made it little bit shorter:
*** Settings ***
Resource resource.robot
*** Test Cases ***
Upload data
Navigate to somewhere
*** Keywords ***
Navigate to somewhere
${Url} = Get location
${Url_import} ${NOuse} = Split String From Right ${Url} / 1
[Return] ${Url_import}
Go to ${Url_import}/somewhere
Related
I am new to this run keyword if method.
I wanted to enter different number based on specific page.
e.g. if page1 element is detected then input number 1, if page2 then input number 2.
*** Settings ***
Library Selenium2Library
Library Collections
Resource ../Resources/nine-res-work.robot
*** Variables ***
${LOGIN-BUTTON-NUMBER-1} ${ANDROID-WIDGET-TEXT-VIEW}\[#resource-id="com.test.abc.work.cac:id/btn_number" and #text="1"]
${LOGIN-BUTTON-NUMBER-2} ${ANDROID-WIDGET-TEXT-VIEW}\[#resource-id="com.test.abc.work.cac:id/btn_number" and #text="2"]
${LOGIN-BUTTON-NUMBER-3} ${ANDROID-WIDGET-TEXT-VIEW}\[#resource-id="com.test.abc.work.cac:id/btn_number" and #text="3"]
${LOGIN-PAGE-HEARDER-page1} ${ANDROID-WIDGET-TEXT-VIEW}\[#resource-id="com.test.abc.work.cac:id/headerText" and #text="Enter your PIN."]
${LOGIN-PAGE-HEARDER-page2} ${ANDROID-WIDGET-TEXT-VIEW}\[#resource-id="com.test.abc.work.cac:id/headerText" and #text="Enter your passcode."]
*** Keywords ***
Smart Card Login
Run Keyword If ${LOGIN-PAGE-HEARDER-page1} == 'PASS' Tap ${LOGIN-BUTTON-NUMBER-1}
Run Keyword If ${LOGIN-PAGE-HEARDER-page2} == 'PASS' Tap ${LOGIN-BUTTON-NUMBER-2}
*** Test Cases ***
Test 1
Launch Application
Smart Card Login
error
Test 1 | FAIL |
Evaluating expression '//android.widget.TextView[#resource-id="com.test.abc.work.cac:id/headerText" and #text="Enter your PIN."] == 'PASS'' failed: SyntaxError: invalid syntax (<string>, line 1)
I have tried another way, this time no error but the tap action isn't execute.
*** Variables ***
${LOGIN-BUTTON-NUMBER-1} ${ANDROID-WIDGET-TEXT-VIEW}\[#resource-id="com.test.abc.work.cac:id/btn_number" and #text="1"]
${LOGIN-BUTTON-NUMBER-2} ${ANDROID-WIDGET-TEXT-VIEW}\[#resource-id="com.test.abc.work.cac:id/btn_number" and #text="2"]
${LOGIN-BUTTON-NUMBER-3} ${ANDROID-WIDGET-TEXT-VIEW}\[#resource-id="com.test.abc.work.cac:id/btn_number" and #text="3"]
${LOGIN-PAGE-HEARDER-page1} ${ANDROID-WIDGET-TEXT-VIEW}\[#text="Enter your PIN."]
${LOGIN-PAGE-HEARDER-page2} ${ANDROID-WIDGET-TEXT-VIEW}\[#text="Enter your passcode."]
*** Keywords ***
Input App Passcode
Tap ${LOGIN-BUTTON-NUMBER-2}
*** Test Cases ***
Launch App
Open Nine Folders Application
Sleep 5s
Input Password
${Page1} = Page Should Contain Element ${LOGIN-PAGE-HEARDER-page1}
Run Keyword If '${Page1}' == 'PASS' Input App Passcode
You got syntax error because Run Keyword If expects a valid Python condition as first argument. This is not the case in your code. In your case this is what happens, assuming ${ANDROID-WIDGET-TEXT-VIEW} is just view in this example:
Run Keyword If ${LOGIN-PAGE-HEARDER-page1} == 'PASS' Tap ${LOGIN-BUTTON-NUMBER-1}
which is
Run Keyword If view\[#resource-id="com.test.abc.work.cac:id/headerText" and #text="Enter your PIN."] == 'PASS' Tap ${LOGIN-BUTTON-NUMBER-1}
this is equivalent with the following Python code:
if view\[#resource-id="com.test.abc.work.cac:id/headerText" and #text="Enter your PIN."] == 'PASS':
call_tap_function(LOGIN_BUTTON_NUMBER_1)
There are a bunch of invalid characters there because the string is not enclosed in '. So correctly it should be:
Run Keyword If '${LOGIN-PAGE-HEARDER-page1}' == 'PASS' Tap ${LOGIN-BUTTON-NUMBER-1}
which will translate to:
if 'view\[#resource-id="com.test.abc.work.cac:id/headerText" and #text="Enter your PIN."]' == 'PASS':
call_tap_function(LOGIN_BUTTON_NUMBER_1)
Note that this will never be equal with PASS.
As for your second approach, Page Should Contain Element does not have a return value, it will fail or the execution continues as usual. To achieve what you want you should use the Run Keyword And Return Status that will return if the keyword called has passed or failed.
Input Password
${Page1} = Run Keyword And Return Status Page Should Contain Element ${LOGIN-PAGE-HEARDER-page1}
Run Keyword If ${Page1} Input App Passcode
Here ${Page1} variable will be true if Page Should Contain Element passed, aka if login page header page1 was on the page.
the app under testing has the page flow possibilities as below:
- app launch -> PAGE1 (input pin1) -> DONE
or
- app launch -> PAGE1 (input pin1) -> PAGE2 (input pin2) -> DONE
or
- app launch -> PAGE2 (input pin2) -> PAGE1 (input pin1) -> DONE
I have written test case to wait for the element then perform different keyword.
*** Settings ***
Library AppiumLibrary
Resource ../Resources/nine-res-work.robot
Resource ../Resources/common-mobile.robot
*** Variables ***
${LOGIN-BUTTON-NUMBER-1} ${ANDROID-WIDGET-TEXT-VIEW}\[#resource-id="${PREFIX-ID}btn_number" and #text="1"]
${LOGIN-BUTTON-NUMBER-2} ${ANDROID-WIDGET-TEXT-VIEW}\[#resource-id="${PREFIX-ID}btn_number" and #text="2"]
${LOGIN-PAGE1} ${ANDROID-WIDGET-TEXT-VIEW}\[#resource-id="${PREFIX-ID}headerText" and #text="Enter your PIN."]
${LOGIN-PAGE2} ${ANDROID-WIDGET-TEXT-VIEW}\[#resource-id="${PREFIX-ID}headerText" and #text="Enter your passcode."]
*** Keywords ***
Input PAGE1 Passcode
Tap ${LOGIN-BUTTON-NUMBER-1}
Input PAGE2 Passcode
Tap ${LOGIN-BUTTON-NUMBER-2}
*** Test Cases ***
Launch App
Launch Application
Sleep 5s
Input Password
${PAGE1}= Run Keyword And Return Status Page Should Contain Element ${LOGIN-PAGE1}
${PAGE2}= Run Keyword And Return Status Page Should Contain Element ${LOGIN-PAGE2}
Run Keyword If ${PAGE1} Input PAGE1 Passcode
Sleep 15s
Run Keyword If ${PAGE2} Input PAGE2 Passcode
Errors occurred at
Page Should Contain Element ${LOGIN-PAGE2} but screenshot is page 1
In this piece here:
${PAGE1}= Run Keyword And Return Status Page Should Contain Element ${LOGIN-PAGE1}
${PAGE2}= Run Keyword And Return Status Page Should Contain Element ${LOGIN-PAGE2}
, you are checking is this LOGIN-PAGE1, and immediately after that - is it 2.
Say it is 1, and the variable is set to True - you are not taking any action before checking for LOGIN-PAGE2, that happens immediately. And reading your description, it can be only one of them - thus what you say is an error is actually the expected state, the screenshot is from page 1.
To fulfill this logic:
- app launch -> PAGE1 (input pin1) -> PAGE2 (input pin2) -> DONE
or
- app launch -> PAGE2 (input pin2) -> PAGE1 (input pin1) -> DONE
, this code should do:
Run Keyword If ${PAGE1} and not ${PAGE2} Run Keywords Input PAGE1 Passcode AND Sleep 15s AND Input PAGE2 Passcode
... ELSE IF not ${PAGE1} and ${PAGE2} Run Keywords Input PAGE2 Passcode AND Sleep 15s AND Input PAGE1 Passcode
... ELSE Fail Unexpected state. # both variables are false, or both - true, which is not by design
There is a customized RF library written in Python:
class ALibrary:
def __init__(self, name):
self._name = name
def get_name(self):
print "*WARN*" + self._name
Import this library within settings
*** Settings ***
Library lib/ALibrary.py LaoWang
by default, it creates new instances of test libraries for every test case.
My understanding is that __init__(self, name) gets invoked at the beginning of every test case, for example:
*** Test Cases ***
Test Case 1
get name
Test Case 2
get name
Robotframework should create one instance for Test Case 1 and another instance for Test Case 2, however, my __init__(self, name) required an argument, how do I pass this argument within the *** Test Cases ***?
Did a test:
$ python -m robot test.1.robot
==============================================================================
Test.1
==============================================================================
[ WARN ] LaoWang
Case 1 | PASS |
------------------------------------------------------------------------------
[ WARN ] LaoWang
Case 2 | PASS |
------------------------------------------------------------------------------
Test.1 | PASS |
2 critical tests, 2 passed, 0 failed
2 tests total, 2 passed, 0 failed
Both cases shown LaoWang, does that mean RF didn't create new instance in different test cases?
As this question is about Test Library Scope, I do want to link to the Robot Framework documentation on this topic. The way it should be read is that the library in the settings is re-initialised in the same way for each test case. Below is an example that illustrates that behaviour:
ALibrary.py
class ALibrary:
ROBOT_LIBRARY_SCOPE = 'TEST CASE'
def __init__(self, name):
self._name = name
def get_name(self):
print "*WARN*" + self._name
def set_name(self, name):
self._name = name
ALibrary.robot
*** Settings ***
Library ALibrary LaoWang
*** Test Cases ***
Test Case 1
get name
set name New Name
get name
Test Case 2
get name
set name Another New Name
get name
In the below result you can see that even after setting the new name in the first test case the default name from the settings initialisation returns in the second test case.
==============================================================================
TestProject
==============================================================================
TestProject.ALibrary
==============================================================================
Test Case 1
[ WARN ] LaoWang
[ WARN ] New Name
| PASS |
------------------------------------------------------------------------------
Test Case 2
[ WARN ] LaoWang
[ WARN ] Another New Name
| PASS |
------------------------------------------------------------------------------
TestProject.ALibrary | PASS |
2 critical tests, 2 passed, 0 failed
2 tests total, 2 passed, 0 failed
==============================================================================
For Robotframework, in given testcase code 1 and code 2 to access dictionary object. Problem is that when I use json.load to convert my json object which returns a list, returns json keys in single ' instead of double comma " object and when i don't use json.load it returns Unicode error
define library
*** Settings ***
Library OperatingSystem
Library Collections
Library HttpLibrary.HTTP
*** Test Cases ***
Code1
#get json file
${json_data}= Get file detail.json
#get dictionaries under list
${valuelist}= Get Json Value ${json_data} /alladdress/addresslist
# display it
log to console ${valuelist}
# loop over dictionaries under list
: FOR ${key} in #{valuelist.keys()}
\ ${value}= Get From Dictionary ${valuelist} ${key}
# getting AttributeError: 'unicode' object has no attribute 'keys
\ log to console ${key},${value}
Code2
# get json file
${json_data}= Get file detail.json
# get dictionaries under list
${valuelist}= Get Json Value ${json_data} /alladdress/addresslist
# use below line to avoid unicode error
${obj_list}= evaluate json.loads('''${valuelist}''') json
# display it
log to console ${obj_list}
# loop over dictionaries under list
: FOR ${key} in #{obj_list.keys()}
\ ${value}= Get From Dictionary ${obj_list} ${key}
# getting AttributeError: 'list' object has no attribute 'keys'
\ log to console ${key},${value}
here is json file
{
"class":{
"id":0,
"name":"David"
},
"alladdress":{
"count":3,
"addresslist":[
{
"houseno":1,
"streetno":5,
"streetname":"tesla",
"city":"ABC",
"state":"AA",
"country":"UK",
"zip":85555
},
{
"houseno":2,
"streetno":6,
"streetname":"honda",
"city":"PQR",
"state":"BB",
"country":"IN",
"zip":5252
}
]
}
}
In the HttpLibrary Library there is the Parse JSON keyword that is of use here. It can convert the string of the JSON document that is fetched using Get JSON Value into a dictionary.
So the value here is that you don't have to 'walk' the dictionary to get to the node you're looking for.
*** Settings ***
Library OperatingSystem
Library HttpLibrary.HTTP
*** Test Cases ***
Fetch Address List
${json_data}= Get file details.json
${addressesJSONstring} Get Json Value ${json_data} /alladdress/addresslist
${addresseslist} Parse Json ${addressesJSONstring}
: FOR ${addressDict} in #{addresseslist}
\ log ${addressDict['country']}
It appears that the keyword Get json value is returning strings rather than objects. If you replace that call with code that uses python's json module, you can then parse the data to find what you want.
For example, this will print out each address dictionary:
*** Test Cases ***
Code1
#get json file
${json_data}= Get file detail.json
#get dictionaries under list
${data}= evaluate json.loads($json_data) json
${alladdress}= get from dictionary ${data} alladdress
${addresslist}= get from dictionary ${alladdress} addresslist
# loop over dictionaries under list
log to console addresses:
: FOR ${address} in #{addresslist}
\ log to console ${address}
I have a file that goes like this:
Name: John
Class: II
Age: 8
Interest: Sports
Name: Emma
Class: III
Hobby: Dance
So I want to read this file and put the contents into a dictionary with Name as key. The sections vary in number of lines. How can I achieve this dictionary using Robot Framework keywords.
Is this what you need?
*** Settings ***
Library OperatingSystem
Library String
Library Collections
*** Test Cases ***
Split File By Names
${my_dict} Create Dictionary
${data} Get File <path_to_your_data>
#{lines} Split To Lines ${data}
Remove Values From List ${lines} ${EMPTY}
:FOR ${line} IN #{lines}
\ ${key} ${value} Split String ${line} :
\ ${name} Set Variable If '${key}' == 'Name' ${value.strip()} ${name}
\ Run Keyword If '${key}' == 'Name' Set To Dictionary ${my_dict} ${name}=#{EMPTY}
\ Run Keyword If '${key}' <> 'Name' Append To List ${my_dict.${name}} ${line}
Log ${my_dict}
Anyway, the RF way to parse the file sucks. I would rather go for python.
#!/usr/bin/python
# -*- coding: utf-8 -*-
class ParseFile:
def __init__(self):
self.my_dict = {}
def parse_file_to_dict(self):
with open('<path_to_your_data>') as f:
lines = f.read().splitlines()
for line in (l for l in lines if l != ""):
key, value = line.split(":", 1)
if key == "Name":
name = value
self.my_dict[name] = []
else:
self.my_dict[name].append(line)
return self.my_dict
... and then just call it in RF.
*** Settings ***
Library ParseFile.py
*** Test Cases ***
Do It In Python
${my_dict} Parse File To Dict
Log ${my_dict}
Please note that both ways are strictly tight to data structure you provided. I.e. if "Name" is not at the first line of each section, it will not work and will need more handling with the data.