Trying to use Append To File raises AttributeError exception - robotframework

I am trying to append data stored in a variable to a file. The file was successfully created and the data is stored in the variable; however, I continue to get the AttiributeError:
AttributeError: 'NoneType' object has no attribute 'replace'
here are my results
KEYWORD ${FileA} = OperatingSystem . Create File metrics/Live_RTT.txt, ${MT_Com_RTT_LB_Live_Topics}
00:00:05.233 KEYWORD Selenium2Library . Click Element ${BLANK_CANVAS}
00:00:05.129 KEYWORD Selenium2Library . Click Element //div[#title='${device1}']
00:00:05.063 KEYWORD Selenium2Library . Click Element //div[#title='${device2}']
00:00:00.045 KEYWORD ${MT_Com_RTT_LB_Live_Topics} = Selenium2Library . Get Text xpath=${REAL_TIME_TRENDING_TOPICS}
00:00:00.001 KEYWORD BuiltIn . Log ${MT_Com_RTT_LB_Live_Topics}
Documentation:
Logs the given message with the given level.
Start / End / Elapsed: 20181028 21:37:34.971 / 20181028 21:37:34.972 / 00:00:00.001
21:37:34.971 INFO Available Topics
Active Energy Delivered + Received
Active Energy Into the Load
Active Energy Out of the Load
Active Power
Active Power A
00:00:00.001 KEYWORD OperatingSystem . Append To File ${FileA}, ${MT_Com_RTT_LB_Live_Topics}
Documentation:
Appends the given content to the specified file.
Start / End / Elapsed: 20181028 21:37:34.972 / 20181028 21:37:34.973 / 00:00:00.001
21:37:34.973 FAIL AttributeError: 'NoneType' object has no attribute 'replace'

Looking at the library's source, the exception most probably is raised in the normalize_path() method, which does this string replacement:
path = os.path.normpath(os.path.expanduser(path.replace('/', os.sep)))
, where path is the filename - the first argument to the Append To File kw call. The exception says its value is None (e.g. no value set) while it should be some string.
Looking at the log, you're setting this value to the return value of Create File - but that keyword does not return one at all, it simply creates the file you specified in its first argument.
So to resolve - just set the value of ${FileA} yourself, pass it to the two keywords, and do not reassign in the Create File call.

Related

Cannot input text or Press Keys or Press Keycode

I am very new to robot framework, working it at pycharm.
I just want to input some text at current field. but none of these works.
I have tried
Press Keycode a
Press Keys text_field q
input text ${mylocator} ${string-abc}
If I am using the correct library?
*** Settings ***
Library AppiumLibrary run_on_failure=AppiumLibrary.CapturePageScreenshot
Library Selenium2Library run_on_failure=AppiumLibrary.CapturePageScreenshot
*** Variables ***
${MsgEditor} //*[#class='android.widget.RelativeLayout' and #resource-id='com.test.abc.work.cac:id/editor']
*** Test Cases ***
Add item
Press Keys text_field q <--error browser not open
\\or
appiumlibrary.Input Text ${MsgEditor} update <== error Message: Cannot set the element to 'update'. Did you interact with the correct element?
updated that Press Keys still failed. error:
KEYWORD Selenium2Library . Press Keys text_field, q
Documentation:
Simulates the user pressing key(s) to an element or on the active browser.
Start / End / Elapsed: 20201218 10:05:20.575 / 20201218 10:05:21.339 / 00:00:00.764
00:00:00.755KEYWORD AppiumLibrary . Capture Page Screenshot
10:05:20.575 TRACE Arguments: [ 'text_field' | 'q' ]
10:05:20.581 INFO Sending key(s) ('q',) to text_field element.
10:05:21.339 FAIL No browser is open.
10:05:21.339 DEBUG Traceback (most recent call last):
File "c:\users\tester\pycharmprojects\androidtest\venv\lib\site-packages\SeleniumLibrary\__init__.py", line 471, in run_keyword
return DynamicCore.run_keyword(self, name, args, kwargs)
File "c:\users\tester\pycharmprojects\androidtest\venv\lib\site-packages\robotlibcore.py", line 103, in run_keyword
return self.keywords[name](*args, **(kwargs or {}))
File "c:\users\tester\pycharmprojects\androidtest\venv\lib\site-packages\SeleniumLibrary\keywords\element.py", line 895, in press_keys
element = self.find_element(locator)
File "c:\users\tester\pycharmprojects\androidtest\venv\lib\site-packages\SeleniumLibrary\base\context.py", line 74, in find_element
return self.element_finder.find(locator, tag, True, required, parent)
File "c:\users\tester\pycharmprojects\androidtest\venv\lib\site-packages\SeleniumLibrary\locators\elementfinder.py", line 74, in find
parent=parent or self.driver)
File "c:\users\tester\pycharmprojects\androidtest\venv\lib\site-packages\SeleniumLibrary\base\context.py", line 32, in driver
return self.ctx.driver
File "c:\users\tester\pycharmprojects\androidtest\venv\lib\site-packages\SeleniumLibrary\__init__.py", line 547, in driver
raise NoOpenBrowser('No browser is open.')
InputText failed. I have this same InputText at other field and works. I tried tap over different field with that locator and it works.
is the app has issue on that element? but i can press on keyboard manually after tap to the field.
I recommend reading the official documentation first: http://serhatbolsu.github.io/robotframework-appiumlibrary/AppiumLibrary.html and the user guide as well: https://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html
Press Keys is not a keyword from AppiumLibrary. It's a keyword from SeleniumLibrary (https://robotframework.org/SeleniumLibrary/SeleniumLibrary.html#Press%20Keys), but you're not importing it to your project. And even if you imported it, this keyword takes two arguments (locator and keys), so you need to use it correctly.
*** Test Cases ***
Press Keys a
In addition to what I've said, there's no test case name, this is incorrect and won't work, it will in fact give you the following error:
Test case name cannot be empty.
You have correctly used Input Test keyword from AppiumLibrary. However, you might need to validate that your locator is correct. We can't say that since you didn't include that information in your question.
I have used another method. Adb shell input text to fire the text

Joining 3 strings and using them to find element not working

I've a situation in my app where I need to click on file name from a /div table which will save the file name internally and gets displayed in a /div grid. Every time I run the automation, I need to click on a new file.
I don't have any problem in clicking the file to be saved.
But after the click, the app stores the file in a /div grid and I want to select the file that I added.
For this I used this approach:
I am storing the file name in a global variable ${g_ExtractedFileName}
For eg: XX_YY_Response_IdNum_48015_2020-07-27T12-18-44.334442.txt
My complete Xpath to the file is something like this
xpath://div[#role='row']//span[contains(text(),'XX_YY_Response_IdNum_48015_2020-07-27T12-18-44.334442.txt')]
For this I have the below:-
${attachedFileXpath1}= set variable xpath://div[#role='row']//span[contains(text(),'
${attachedFileXpath2}= set variable ${g_ExtractedFileName}
${attachedFileXpath3}= set variable ')]'
${attachedFileXpath}= set variable ${attachedFileXpath1}${attachedFileXpath2}${attachedFileXpath3}
click element ${attachedFileXpath}
This throws an error
SyntaxError: Failed to execute 'evaluate' on 'Document': The string
'//div[#role='row']//span[contains(text(),'XX_YY_Response_IdNum_48015_2020-07-27T12-18-44.334442.txt
')]'' is not a valid XPath expression.
When I don't use the variables and simply use (hard coded value for file name)
click element xpath://div[#role='row']//span[contains(text(),'XX_YY_Response_IdNum_48015_2020-07-27T12-18-44.334442.txt')]
It works correctly.
I also tried this but same error
${attachedFileXpath}= catenate ${attachedFileXpath1}${attachedFileXpath2}${attachedFileXpath3}
I am not sure when the 3 variables are concatenated, it cuts off the variable value to a new line causing this problem.
Any help is much appreciated.
The simplest approach is to simply use the replace functionality that is already present in Robot Framework variables. The below example will result in an Element not found error, but that is expected.
*** Settings ***
Library SeleniumLibrary
*** Variables ***
${g_ExtractedFileName} XX_YY_Response_IdNum_48015_2020-07-27T12-18-44.334442.txt
*** Test Cases ***
Use Global String in Locator
Open Browser https://google.com chrome
click element xpath://div[#role='row']//span[contains(text(),'${g_ExtractedFileName}')]
[Teardown] Close All Browsers
Simply create keyword which accept the file name as argument and returns complete valid xpath expression. It will be re-usable and elegant solution -
*** Test case ***
Select file saved in div grid
${attached_file_xpath}= GetAttachedFileXpath ${File_Name}
Click Element ${attached_file_xpath}
*** Keywords ***
GetAttachedFileXpath [Arguments] ${File_Name}
return from keyword xpath://div[#role='row']//span[contains(text(),'${File_Name}')]

RF 3.0.4 nested dictionary syntax error if first key is number, dot notation doesn't work

I am using rf 3.0.4. I upgraded because of the dot notation upgrade (before I was using rf 2.9).
My problem is when I want to access a nested dictionary item and the first key (it is an id from db) is a number, I got a syntax error.
I have a nested dictionary: &{Attributes}
So what I want to do:
${Attributes.1000.name}
The syntax error I get:
Resolving variable '${Attributes.1000.name}' failed: SyntaxError: invalid syntax (<string>, line 1)
And what is working:
${Attributes["1000"]["name"]}
I'd like to use the dot notation, because it is more readable.
Do any of you know why it doesn't work?
It seems to me to be a limitation of Robot Framework. When a dictionary key item starts with a number (even when a string) then it will fail. In the below two test cases this is shown.
To me this sounds like a defect and you may want to log this as an issue with the project's GitHub issue log.
*** Settings ***
Library Collections
*** Variables ***
${name} MyName
&{person} name=${name}
&{person_valid} A1000=${person} A2000=${person}
&{person_invalid} 1000A=${person} 2000A=${person}
*** Test Cases ***
TC - Valid
${pers} Set Variable ${person_valid.A1000}
Dictionaries Should Be Equal ${pers} ${person}
${pers_name_1} Set Variable ${person_valid["A1000"]["name"]}
Should Be Equal As Strings ${pers_name_1} ${name}
${pers_name_2} Set Variable ${person_valid.A1000.name}
Should Be Equal As Strings ${pers_name_2} ${name}
TC - Fails
Run Keyword And Expect Error
... Resolving variable '\${person_invalid.1000A}' failed: SyntaxError: invalid syntax (<string>, line 1)
... Set Variable ${person_invalid.1000A}
Run Keyword And Expect Error
... Resolving variable '\${person_valid.1000A.name}' failed: SyntaxError: invalid syntax (<string>, line 1)
... Set Variable ${person_valid.1000A.name}

How to check if a substring exists inside a 'Result' object with Robot Framework?

I am running the following test inside Robot Framework:
*** Settings ***
Documentation This initializes testrun.robot
Library Process
Library OperatingSystem
Library XML
Library Collections
Output Is A Valid XML File Against JATS format
Start Process xmllint --dtdvalid http://jats.nlm.nih.gov/archiving/1.1d3/JATS-archivearticle1.dtd ./output/nlm/out.xml shell=True
${result}= Wait For Process timeout=45 secs
Log ${result.stdout}
Log ${result.stderr}
Run Keyword Unless '${result.stderr} == ${EMPTY}' Should Contain ${result.stderr} element xref: validity error : IDREFS attribute rid references an unknown
The variable ${result.stderr} is a string that contains the substring 'element xref: validity error : IDREFS attribute rid references an unknown'. As far as I know, I'm not dealing with any whitespace errors or quotation problems, although I could be wrong. (I've been fiddling around with that for a while now.)
Thanks for the help!
Edit: The log that Robot Framework generates tells me that the process has finished (that's how I know what result.stderr contains.)
Consider this statement:
Run Keyword Unless '${result.stderr} == ${EMPTY}' ...
The keyword will never run because you aren't comparing two variables, you are simply checking whether the string literal string '${result.stderr} == ${EMPTY}' is not empty (and it's not, because it has 28 characters).
It is the same as if you did this in python:
if len('${result.stderr} == ${EMPTY}') > 0:
...
You need to put the single quotes around each variable separately so that you are passing a valid expression to the if statement, rather than a string that looks like a valid expression:
Run Keyword Unless '${result.stderr}' == '${EMPTY}' ...

PROGRESS - Validating a user-input file output path

I've written some PROGRESS code that outputs some data to a user defined file. The data itself isn't important, the output process works fine. It's basically
DEFINE VARIABLE filePath.
UPDATE filePath /*User types in something like C:\UserAccount\New.txt */
OUTPUT TO (VALUE) filePath.
Which works fine, a txt file is created in the input directory. My question is:
Does progress have any functionality that would allow me to check if an input
file path is valid? (Specifically, if the user has input a valid directory, and if they have permission to create a file in the directory they've chosen)
Any input or feedback would be appreciated.
FILE-INFO
Using the system handle FILE-INFO gives you a lot of information. It also works on directories.
FILE-INFO:FILE-NAME = "c:\temp\test.p".
DISPLAY
FILE-INFO:FILE-NAME
FILE-INFO:FILE-CREATE-DATE
FILE-INFO:FILE-MOD-DATE
FILE-INFO:FILE-INFO
FILE-INFO:FILE-MOD-TIME
FILE-INFO:FILE-SIZE
FILE-NAME:FILE-TYPE
FILE-INFO:FULL-PATHNAME
WITH FRAME f1 1 COLUMN SIDE-LABELS.
A simple check for existing directory with write rights could be something like:
FUNCTION dirOK RETURNS LOGICAL (INPUT pcDir AS CHARACTER):
FILE-INFO:FILE-NAME = pcDir.
IF INDEX(FILE-INFO:FILE-TYPE, "D") > 0
AND INDEX(FILE-INFO:FILE-TYPE, "W") > 0 THEN
RETURN TRUE.
ELSE
RETURN FALSE.
END FUNCTION.
FILE-NAME:FILE-TYPE will start with a D for directories and a F for plain files. It also includes information about reading and writing rights. Check the help for more info. If the file doesn't exist basically all attributes except FILE-NAME will be empty or unknown (?).
Edit: it seems that FILE-TYPE returns W in some cases even if there's no actual writing rights in that directory so I you might need to handle this through error processing instead
ERROR PROCESSING
OUTPUT TO VALUE("f:\personal\test.txt").
PUT UNFORMATTED "Test" SKIP.
OUTPUT CLOSE.
CATCH eAnyError AS Progress.Lang.ERROR:
/* Here you could check for specifically error no 98 indicating a problem opening the file */
MESSAGE
"Error message and number retrieved from error object..."
eAnyError:GetMessage(1)
eAnyError:GetMessageNum(1) VIEW-AS ALERT-BOX BUTTONS OK.
END CATCH.
FINALLY:
END FINALLY.
SEARCH
When checking for a single file the SEARCH command will work. If the file exists it returns the complete path. It does however not work on directory, only files. If you SEARCH without complete path e g SEARCH("test.p") the command will search through the directories set in the PROPATH environment variable and return the first matching entry with complete path. If there's no match it will return unknown value (?).
Syntax:
IF SEARCH("c:\temp\test.p") = ? THEN
MESSAGE "No such file" VIEW-AS ALERT-BOX ERROR.
ELSE
MESSAGE "OK" VIEW-AS ALERT-BOX INFORMATION.
SYSTEM-DIALOG GET-FILE character-field has an option MUST-EXIST if you want to use a dailogue to get filename/dir from user. Example from manual
DEFINE VARIABLE procname AS CHARACTER NO-UNDO.
DEFINE VARIABLE OKpressed AS LOGICAL INITIAL TRUE.
Main:
REPEAT:
SYSTEM-DIALOG GET-FILE procname
TITLE "Choose Procedure to Run ..."
FILTERS "Source Files (*.p)" "*.p",
"R-code Files (*.r)" "*.r"
MUST-EXIST
USE-FILENAME
UPDATE OKpressed.
IF OKpressed = TRUE THEN
RUN VALUE(procname).
ELSE
LEAVE Main.
END.

Resources