1*83a54b2fSSadaf Ebrahimi 2*83a54b2fSSadaf Ebrahimi 3*83a54b2fSSadaf Ebrahimi<html> 4*83a54b2fSSadaf Ebrahimi <head> 5*83a54b2fSSadaf Ebrahimi <title>TestNG</title> 6*83a54b2fSSadaf Ebrahimi 7*83a54b2fSSadaf Ebrahimi <link rel="stylesheet" href="testng.css" type="text/css" /> 8*83a54b2fSSadaf Ebrahimi <link type="text/css" rel="stylesheet" href="http://beust.com/beust.css" /> 9*83a54b2fSSadaf Ebrahimi <script type="text/javascript" src="banner.js"></script> 10*83a54b2fSSadaf Ebrahimi 11*83a54b2fSSadaf Ebrahimi <script type="text/javascript" src="http://beust.com/scripts/shCore.js"></script> 12*83a54b2fSSadaf Ebrahimi <script type="text/javascript" src="http://beust.com/scripts/shBrushJava.js"></script> 13*83a54b2fSSadaf Ebrahimi <script type="text/javascript" src="http://beust.com/scripts/shBrushXml.js"></script> 14*83a54b2fSSadaf Ebrahimi <script type="text/javascript" src="http://beust.com/scripts/shBrushBash.js"></script> 15*83a54b2fSSadaf Ebrahimi <script type="text/javascript" src="http://beust.com/scripts/shBrushPlain.js"></script> 16*83a54b2fSSadaf Ebrahimi <link type="text/css" rel="stylesheet" href="http://beust.com/styles/shCore.css"/> 17*83a54b2fSSadaf Ebrahimi <link type="text/css" rel="stylesheet" href="http://beust.com/styles/shThemeCedric.css"/> 18*83a54b2fSSadaf Ebrahimi <script type="text/javascript"> 19*83a54b2fSSadaf Ebrahimi SyntaxHighlighter.config.clipboardSwf = 'scripts/clipboard.swf'; 20*83a54b2fSSadaf Ebrahimi SyntaxHighlighter.defaults['gutter'] = false; 21*83a54b2fSSadaf Ebrahimi SyntaxHighlighter.all(); 22*83a54b2fSSadaf Ebrahimi </script> 23*83a54b2fSSadaf Ebrahimi 24*83a54b2fSSadaf Ebrahimi <style type="text/css"> 25*83a54b2fSSadaf Ebrahimi /* Set the command-line table option column width. */ 26*83a54b2fSSadaf Ebrahimi #command-line colgroup.option { 27*83a54b2fSSadaf Ebrahimi width: 7em; 28*83a54b2fSSadaf Ebrahimi } 29*83a54b2fSSadaf Ebrahimi </style> 30*83a54b2fSSadaf Ebrahimi </head> 31*83a54b2fSSadaf Ebrahimi<body onLoad="prettyPrint()"> 32*83a54b2fSSadaf Ebrahimi 33*83a54b2fSSadaf Ebrahimi<script type="text/javascript"> 34*83a54b2fSSadaf Ebrahimi displayMenu("selenium.html") 35*83a54b2fSSadaf Ebrahimi</script> 36*83a54b2fSSadaf Ebrahimi 37*83a54b2fSSadaf Ebrahimi<h2 align="center">Selenium and TestNG</h2> 38*83a54b2fSSadaf Ebrahimi 39*83a54b2fSSadaf Ebrahimi<em>This documentation was written by Felipe Knorr Kuhn and is adapted from <a href="http://knorrium.info/2010/08/31/using-testng-to-launch-your-tests-and-the-selenium-server/">a series of articles</a> posted on <a href="http://knorrium.info">his blog</a></em>. 40*83a54b2fSSadaf Ebrahimi 41*83a54b2fSSadaf Ebrahimi 42*83a54b2fSSadaf Ebrahimi<h3>Content</h3> 43*83a54b2fSSadaf Ebrahimi 44*83a54b2fSSadaf Ebrahimi<ol> 45*83a54b2fSSadaf Ebrahimi <li><a href="#modeling">How to use TestNG configuration methods with parameters</a> 46*83a54b2fSSadaf Ebrahimi <li><a href="#configuration_methods">How to configure your test</a> 47*83a54b2fSSadaf Ebrahimi <li><a href="#creating_xml">Creating the XML file for TestNG</a> 48*83a54b2fSSadaf Ebrahimi <li><a href="#launching">Lauching your tests with Eclipse</a> 49*83a54b2fSSadaf Ebrahimi <li><a href="#future">How to make the test design a little better for the future</a> 50*83a54b2fSSadaf Ebrahimi</ol> 51*83a54b2fSSadaf Ebrahimi 52*83a54b2fSSadaf Ebrahimi<h3><a name="#modeling">Modeling your test case</a></h3> 53*83a54b2fSSadaf Ebrahimi 54*83a54b2fSSadaf Ebrahimi<p>Before writing a test case, you need to know how and what will be validated. Let's use the WordPress <a href="http://knorrium.info/2010/05/19/a-java-approach-to-selenium">"Create New Post" test case</a>. 55*83a54b2fSSadaf Ebrahimi 56*83a54b2fSSadaf Ebrahimi<ol> 57*83a54b2fSSadaf Ebrahimi 58*83a54b2fSSadaf Ebrahimi <li>Go to <a href="http://demo.opensourcecms.com/wordpress/wp-login.php">http://demo.opensourcecms.com/wordpress/wp-login.php</a> 59*83a54b2fSSadaf Ebrahimi <li>Enter "admin" in the "Username" field 60*83a54b2fSSadaf Ebrahimi <li>Enter "demo123" in the "Password" field 61*83a54b2fSSadaf Ebrahimi <li>Click on the "Log In" button 62*83a54b2fSSadaf Ebrahimi <li>Verify that the text "Howdy, admin" is present 63*83a54b2fSSadaf Ebrahimi <li>Click on the "Posts" link 64*83a54b2fSSadaf Ebrahimi <li>Click on the "Add New" button 65*83a54b2fSSadaf Ebrahimi <li>Type "Selenium Demo Post" in the title field 66*83a54b2fSSadaf Ebrahimi <li>Click on the "Publish" button 67*83a54b2fSSadaf Ebrahimi <li> Verify that the text "Post published" is present 68*83a54b2fSSadaf Ebrahimi 69*83a54b2fSSadaf Ebrahimi</ol> 70*83a54b2fSSadaf Ebrahimi 71*83a54b2fSSadaf Ebrahimi<p>Considering this scenario, the first thing that comes to mind is creating a long test case that goes through all the steps. This might be a good approach if you are writing a manual test case. However, since we are writing an automated test, we want to write our script as modular as possible to be able to reuse parts of it in future scenarios.</p> 72*83a54b2fSSadaf Ebrahimi 73*83a54b2fSSadaf Ebrahimi<p>This is how I would break down the test:</p> 74*83a54b2fSSadaf Ebrahimi 75*83a54b2fSSadaf Ebrahimi<ol> 76*83a54b2fSSadaf Ebrahimi <li>Launch the WordPress site 77*83a54b2fSSadaf Ebrahimi <li>Open the Admin Login page 78*83a54b2fSSadaf Ebrahimi <li>Enter valid login data 79*83a54b2fSSadaf Ebrahimi <li>Navigate to the Write Post page 80*83a54b2fSSadaf Ebrahimi <li>Write the post 81*83a54b2fSSadaf Ebrahimi <li>Publish the post 82*83a54b2fSSadaf Ebrahimi <li>Verify that it was actually post 83*83a54b2fSSadaf Ebrahimi</ol> 84*83a54b2fSSadaf Ebrahimi 85*83a54b2fSSadaf Ebrahimi<p>Keep in mind that this is just an example. You are free to model your tests in any way you want, as long as they have business value and will validate your business logic.</p> 86*83a54b2fSSadaf Ebrahimi<p>Let's see how to do that with actual Java code:</p> 87*83a54b2fSSadaf Ebrahimi 88*83a54b2fSSadaf Ebrahimi<pre class="brush:java"> 89*83a54b2fSSadaf Ebrahimi@Test(description="Launches the WordPress site") 90*83a54b2fSSadaf Ebrahimipublic void launchSite(){ 91*83a54b2fSSadaf Ebrahimi selenium.open(""); 92*83a54b2fSSadaf Ebrahimi selenium.waitForPageToLoad("30000"); 93*83a54b2fSSadaf Ebrahimi assertEquals(selenium.getTitle(), "Demo | Just another WordPress site"); 94*83a54b2fSSadaf Ebrahimi} 95*83a54b2fSSadaf Ebrahimi 96*83a54b2fSSadaf Ebrahimi@Test(description="Navigates to the admin page") 97*83a54b2fSSadaf Ebrahimi public void openAdminPage() { 98*83a54b2fSSadaf Ebrahimi selenium.open("wp-admin"); 99*83a54b2fSSadaf Ebrahimi selenium.waitForPageToLoad("30000"); 100*83a54b2fSSadaf Ebrahimi assertEquals(selenium.getTitle(), "Demo › Log In"); 101*83a54b2fSSadaf Ebrahimi} 102*83a54b2fSSadaf Ebrahimi 103*83a54b2fSSadaf Ebrahimi@Test(description="Enters valid login data") 104*83a54b2fSSadaf Ebrahimi public void loginAsAdmin() { 105*83a54b2fSSadaf Ebrahimi selenium.type("user_login", "admin"); 106*83a54b2fSSadaf Ebrahimi selenium.type("user_pass", "demo123"); 107*83a54b2fSSadaf Ebrahimi selenium.click("wp-submit"); 108*83a54b2fSSadaf Ebrahimi selenium.waitForPageToLoad("30000"); 109*83a54b2fSSadaf Ebrahimi assertTrue(selenium.isTextPresent("Howdy, admin")); 110*83a54b2fSSadaf Ebrahimi} 111*83a54b2fSSadaf Ebrahimi 112*83a54b2fSSadaf Ebrahimi@Test(description="Navigates to the New Post screen") 113*83a54b2fSSadaf Ebrahimipublic void navigateNewPost() { 114*83a54b2fSSadaf Ebrahimi selenium.click("//a[contains(text(),'Posts')]/following::a[contains(text(),'Add New')][1]"); 115*83a54b2fSSadaf Ebrahimi selenium.waitForPageToLoad("30000"); 116*83a54b2fSSadaf Ebrahimi assertTrue(selenium.isTextPresent("Add New Post")); 117*83a54b2fSSadaf Ebrahimi} 118*83a54b2fSSadaf Ebrahimi 119*83a54b2fSSadaf Ebrahimi@Test(description="Writes the new post") 120*83a54b2fSSadaf Ebrahimipublic void writeBlogPost() { 121*83a54b2fSSadaf Ebrahimi selenium.type("title", "New Blog Post"); 122*83a54b2fSSadaf Ebrahimi selenium.click("edButtonHTML"); 123*83a54b2fSSadaf Ebrahimi selenium.type("content", "This is a new post"); 124*83a54b2fSSadaf Ebrahimi //TODO:Assert 125*83a54b2fSSadaf Ebrahimi} 126*83a54b2fSSadaf Ebrahimi 127*83a54b2fSSadaf Ebrahimi@Test(description="Publishes the post") 128*83a54b2fSSadaf Ebrahimipublic void publishBlogPost() { 129*83a54b2fSSadaf Ebrahimi selenium.click("submitdiv"); 130*83a54b2fSSadaf Ebrahimi selenium.click("publish"); 131*83a54b2fSSadaf Ebrahimi selenium.waitForPageToLoad("30000"); 132*83a54b2fSSadaf Ebrahimi assertTrue(selenium.isTextPresent("Post published.")); 133*83a54b2fSSadaf Ebrahimi} 134*83a54b2fSSadaf Ebrahimi 135*83a54b2fSSadaf Ebrahimi@Test(description="Verifies the post") 136*83a54b2fSSadaf Ebrahimipublic void verifyBlogPost() { 137*83a54b2fSSadaf Ebrahimi selenium.click("//a[contains(text(),'Posts') and contains(@class,'wp-first-item')]"); 138*83a54b2fSSadaf Ebrahimi selenium.waitForPageToLoad("30000"); 139*83a54b2fSSadaf Ebrahimi assertTrue(selenium.isElementPresent("//a[text()='New Blog Post']")); 140*83a54b2fSSadaf Ebrahimi} 141*83a54b2fSSadaf Ebrahimi 142*83a54b2fSSadaf Ebrahimi@Test(description="Logs out") 143*83a54b2fSSadaf Ebrahimipublic void logout() { 144*83a54b2fSSadaf Ebrahimi selenium.click("//a[text()='Log Out']"); 145*83a54b2fSSadaf Ebrahimi //TODO:Assert 146*83a54b2fSSadaf Ebrahimi} 147*83a54b2fSSadaf Ebrahimi</pre> 148*83a54b2fSSadaf Ebrahimi 149*83a54b2fSSadaf Ebrahimi<p>These are the test methods (or steps) we are going to use. 150*83a54b2fSSadaf Ebrahimi 151*83a54b2fSSadaf Ebrahimi<h3><a name="configuration_methods">Configuration methods</a></h3> 152*83a54b2fSSadaf Ebrahimi 153*83a54b2fSSadaf Ebrahimi<p>If you are familiar with unit testing frameworks, you probably know about the setup and teardown methods. TestNG goes beyond that idea and allows you to define methods that will be run after or before your test suites, test groups or test methods. This is very useful for our Selenium tests because you can create a Selenium server and browser instance before you start running your test suite.)</p> 154*83a54b2fSSadaf Ebrahimi 155*83a54b2fSSadaf Ebrahimi<p>To achieve this, we will use two TestNG <a href="http://testng.org/doc/documentation-main.html#annotations">annotations</a>: <tt>@BeforeSuite</tt> and <tt>@AfterSuite</tt>:</p> 156*83a54b2fSSadaf Ebrahimi 157*83a54b2fSSadaf Ebrahimi<pre class="brush:java "> 158*83a54b2fSSadaf Ebrahimi@BeforeSuite(alwaysRun = true) 159*83a54b2fSSadaf Ebrahimipublic void setupBeforeSuite(ITestContext context) { 160*83a54b2fSSadaf Ebrahimi String seleniumHost = context.getCurrentXmlTest().getParameter("selenium.host"); 161*83a54b2fSSadaf Ebrahimi String seleniumPort = context.getCurrentXmlTest().getParameter("selenium.port"); 162*83a54b2fSSadaf Ebrahimi String seleniumBrowser = context.getCurrentXmlTest().getParameter("selenium.browser"); 163*83a54b2fSSadaf Ebrahimi String seleniumUrl = context.getCurrentXmlTest().getParameter("selenium.url"); 164*83a54b2fSSadaf Ebrahimi 165*83a54b2fSSadaf Ebrahimi RemoteControlConfiguration rcc = new RemoteControlConfiguration(); 166*83a54b2fSSadaf Ebrahimi rcc.setSingleWindow(true); 167*83a54b2fSSadaf Ebrahimi rcc.setPort(Integer.parseInt(seleniumPort)); 168*83a54b2fSSadaf Ebrahimi 169*83a54b2fSSadaf Ebrahimi try { 170*83a54b2fSSadaf Ebrahimi server = new SeleniumServer(false, rcc); 171*83a54b2fSSadaf Ebrahimi server.boot(); 172*83a54b2fSSadaf Ebrahimi } catch (Exception e) { 173*83a54b2fSSadaf Ebrahimi throw new IllegalStateException("Can't start selenium server", e); 174*83a54b2fSSadaf Ebrahimi } 175*83a54b2fSSadaf Ebrahimi 176*83a54b2fSSadaf Ebrahimi proc = new HttpCommandProcessor(seleniumHost, Integer.parseInt(seleniumPort), 177*83a54b2fSSadaf Ebrahimi seleniumBrowser, seleniumUrl); 178*83a54b2fSSadaf Ebrahimi selenium = new DefaultSelenium(proc); 179*83a54b2fSSadaf Ebrahimi selenium.start(); 180*83a54b2fSSadaf Ebrahimi} 181*83a54b2fSSadaf Ebrahimi 182*83a54b2fSSadaf Ebrahimi@AfterSuite(alwaysRun = true) 183*83a54b2fSSadaf Ebrahimipublic void setupAfterSuite() { 184*83a54b2fSSadaf Ebrahimi selenium.stop(); 185*83a54b2fSSadaf Ebrahimi server.stop(); 186*83a54b2fSSadaf Ebrahimi} 187*83a54b2fSSadaf Ebrahimi</pre> 188*83a54b2fSSadaf Ebrahimi 189*83a54b2fSSadaf Ebrahimi<p>PS: Did you notice those weird parameters? They are stored in the XML file (we are going to see in the next section) and accessed by a <tt>ITestContext</tt> object, which was <a href="http://testng.org/doc/documentation-main.html#dependency-injection">injected</a>. </p> 190*83a54b2fSSadaf Ebrahimi 191*83a54b2fSSadaf Ebrahimi<p>By adding these annotations, the TestNG engine will invoke the configuration methods automatically before/after your test suite (make sure the test methods are annotated with <tt>@Test</tt>), launching the Selenium server and instantiating the Selenium client object only once, reusing the same browser session across the tests.</p> 192*83a54b2fSSadaf Ebrahimi 193*83a54b2fSSadaf Ebrahimi<h3><a name="creating_xml">Creating the XML file</a></h3> 194*83a54b2fSSadaf Ebrahimi 195*83a54b2fSSadaf Ebrahimi<p>To define the order of the tests, we will have to create an XML file listing the test methods we would like to run. Make sure that the test methods are annotated with <tt>@Test</tt>, or else the TestNG engine will not invoke them.</p> 196*83a54b2fSSadaf Ebrahimi 197*83a54b2fSSadaf Ebrahimi<p>Before TestNG 5.13.1, you had to use Method Interceptors if you wanted to run the tests in the order defined in the XML file. I have posted <a href="http://gist.github.com/416310">my implementation of a Method Interceptor</a> on my Github account. From TestNG 5.13.1+, you can just add the <tt>preserve-order</tt> parameter to your test tag and include the methods you would like to run, reducing unecessary code in your test suite.</p> 198*83a54b2fSSadaf Ebrahimi 199*83a54b2fSSadaf Ebrahimi<p>Here is the XML file:</p> 200*83a54b2fSSadaf Ebrahimi 201*83a54b2fSSadaf Ebrahimi<pre class="brush: xml"> 202*83a54b2fSSadaf Ebrahimi<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd"> 203*83a54b2fSSadaf Ebrahimi 204*83a54b2fSSadaf Ebrahimi<suite name="Knorrium.info - Wordpress Demo" verbose="10"> 205*83a54b2fSSadaf Ebrahimi <parameter name="selenium.host" value="localhost" /> 206*83a54b2fSSadaf Ebrahimi <parameter name="selenium.port" value="3737" /> 207*83a54b2fSSadaf Ebrahimi <parameter name="selenium.browser" value="*firefox" /> 208*83a54b2fSSadaf Ebrahimi <parameter name="selenium.url" value="http://demo.opensourcecms.com/wordpress/" /> 209*83a54b2fSSadaf Ebrahimi 210*83a54b2fSSadaf Ebrahimi <test name="Write new post" preserve-order="true"> 211*83a54b2fSSadaf Ebrahimi <classes> 212*83a54b2fSSadaf Ebrahimi <class name="test.Wordpress"> 213*83a54b2fSSadaf Ebrahimi <methods> 214*83a54b2fSSadaf Ebrahimi <include name="launchSite" /> 215*83a54b2fSSadaf Ebrahimi <include name="openAdminPage" /> 216*83a54b2fSSadaf Ebrahimi <include name="loginAsAdmin" /> 217*83a54b2fSSadaf Ebrahimi <include name="navigateNewPost" /> 218*83a54b2fSSadaf Ebrahimi <include name="writeBlogPost" /> 219*83a54b2fSSadaf Ebrahimi <include name="publishBlogPost" /> 220*83a54b2fSSadaf Ebrahimi <include name="verifyBlogPost" /> 221*83a54b2fSSadaf Ebrahimi </methods> 222*83a54b2fSSadaf Ebrahimi </class> 223*83a54b2fSSadaf Ebrahimi </classes> 224*83a54b2fSSadaf Ebrahimi </test> 225*83a54b2fSSadaf Ebrahimi</suite> 226*83a54b2fSSadaf Ebrahimi</pre> 227*83a54b2fSSadaf Ebrahimi 228*83a54b2fSSadaf Ebrahimi<h3><a name="launching">Launching your tests in Eclipse</a></h3> 229*83a54b2fSSadaf Ebrahimi 230*83a54b2fSSadaf Ebrahimi<p>We finished writing our tests, now how can we run them?</p> 231*83a54b2fSSadaf Ebrahimi<p>You can launch TestNG from the command line, using a Eclipse plugin or even programatically. We are going to use the Eclipse plugin. Follow the steps described on the official TestNG documentation <a href="http://testng.org/doc/download.html">over here</a></p> 232*83a54b2fSSadaf Ebrahimi 233*83a54b2fSSadaf Ebrahimi<p>If you installed TestNG correctly, you will see this menu when you right click on the XML file:<br /> 234*83a54b2fSSadaf Ebrahimi 235*83a54b2fSSadaf Ebrahimi<p align="center"> 236*83a54b2fSSadaf Ebrahimi <a href="http://testng.org/pictures/testNG-run.png"> 237*83a54b2fSSadaf Ebrahimi <img src="http://testng.org/pictures/testNG-run.png" alt="" title="testNG-run" width="464" height="59" class="aligncenter size-full wp-image-19" /> 238*83a54b2fSSadaf Ebrahimi</a> 239*83a54b2fSSadaf Ebrahimi</p> 240*83a54b2fSSadaf Ebrahimi 241*83a54b2fSSadaf Ebrahimi<p>Click on “Run as TestNG Suite” and your test will start running. You will then see this nice results tree:</p> 242*83a54b2fSSadaf Ebrahimi 243*83a54b2fSSadaf Ebrahimi<p align="center"> 244*83a54b2fSSadaf Ebrahimi <a href="http://testng.org/pictures/testNG-exec.png"> 245*83a54b2fSSadaf Ebrahimi <img src="http://testng.org/pictures/testNG-exec.png" alt="" title="testNG-exec" width="591" height="256" class="aligncenter size-full wp-image-20" /></a> 246*83a54b2fSSadaf Ebrahimi</p> 247*83a54b2fSSadaf Ebrahimi 248*83a54b2fSSadaf Ebrahimi<h3><a name="future">Thinking about the future</a></h3> 249*83a54b2fSSadaf Ebrahimi 250*83a54b2fSSadaf Ebrahimi 251*83a54b2fSSadaf Ebrahimi<p>If you really want to think about the future of your test suite, I would recommend you to read <a href="http://adam.goucher.ca/">Adam</a> <a href="http://twitter.com/adamgoucher">Goucher’s</a> <a href="http://www.pragprog.com/magazines">article</a> published on PragPub. He talks about Selenium 2 and the Page Objects Model (a very nice way to model your tests, especially if you use Selenium 2).</p> 252*83a54b2fSSadaf Ebrahimi 253*83a54b2fSSadaf Ebrahimi<p>Since there are lots of people still using Selenium 1, I'll stick to that for a while, but Selenium 2 will eventually be covered here.</p> 254*83a54b2fSSadaf Ebrahimi 255*83a54b2fSSadaf Ebrahimi<p>As the number of tests in your test suite grows, you will find that grouping them in different test classes is a good idea. If you do that, you can take advantage of object oriented programming and create a new class named BaseTest (for example), and leave your configuration logic there. That way, every test class must extend the BaseTest class and use static attributes.</p> 256*83a54b2fSSadaf Ebrahimi 257*83a54b2fSSadaf Ebrahimi 258*83a54b2fSSadaf Ebrahimi<pre class="brush: java"> 259*83a54b2fSSadaf Ebrahimipublic class WordPressAdmin extends BaseTest { 260*83a54b2fSSadaf Ebrahimi@Test 261*83a54b2fSSadaf Ebrahimipublic void test1(){ 262*83a54b2fSSadaf Ebrahimi selenium.open(""); 263*83a54b2fSSadaf Ebrahimi //... 264*83a54b2fSSadaf Ebrahimi} 265*83a54b2fSSadaf Ebrahimi 266*83a54b2fSSadaf Ebrahimi@Test 267*83a54b2fSSadaf Ebrahimipublic void test2(){ 268*83a54b2fSSadaf Ebrahimi selenium.open(""); 269*83a54b2fSSadaf Ebrahimi //... 270*83a54b2fSSadaf Ebrahimi} 271*83a54b2fSSadaf Ebrahimi} 272*83a54b2fSSadaf Ebrahimi</pre> 273*83a54b2fSSadaf Ebrahimi 274*83a54b2fSSadaf Ebrahimi<p>This is better than leaving your configuration methods in the test class.</p> 275*83a54b2fSSadaf Ebrahimi 276*83a54b2fSSadaf Ebrahimi 277