├── LICENSE ├── README.md ├── pom.xml ├── src ├── main │ ├── java │ │ └── com │ │ │ └── force │ │ │ ├── base │ │ │ ├── DriverFactory.java │ │ │ ├── PlaywrightWrapper.java │ │ │ └── ProjectHooks.java │ │ │ ├── config │ │ │ ├── BrowserConfig.java │ │ │ ├── Configuration.java │ │ │ └── ConfigurationManager.java │ │ │ ├── data │ │ │ └── dynamic │ │ │ │ └── FakerDataFactory.java │ │ │ ├── enums │ │ │ └── Country.java │ │ │ └── utility │ │ │ ├── DataLibrary.java │ │ │ ├── HtmlReporter.java │ │ │ ├── MediaEntityBuilder.java │ │ │ ├── RetryTests.java │ │ │ └── TempMailer.java │ └── resources │ │ ├── app.properties │ │ ├── grid.properties │ │ ├── local.properties │ │ └── report.properties └── test │ └── java │ ├── page │ ├── base │ │ ├── HomePage.java │ │ ├── LoginPage.java │ │ └── SalesforceHooks.java │ └── services │ │ ├── AccountPage.java │ │ ├── LeadPage.java │ │ ├── ServiceDashboardPage.java │ │ └── UsersPage.java │ └── tests │ └── ui │ └── services │ ├── TC01_CreateAccount.java │ ├── TC02_EditAccount.java │ ├── TC03_CreateLead.java │ └── TC04_EditLead.java ├── storage └── login.json └── testng.xml /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 TestLeaf 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Simba - Playwright Automation Framework 2 | 3 | Simba is Java based clean code playwright automation framework architected using several useful design patterns. 4 | 5 | ## Software Dependencies 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 |
DependencyVersionFeature
Playwright1.28.0End to End Automation - Browser & API
TestNG7.4.0The Test Runner to execute suite
JSON20220924Create & Parse JSON Files for API
Apache POI5.2.3Read Excel files for test data
Data Faker1.6.0Create runtime fake test data
Owner1.0.12Minimize the properties file code
Extent Reports3.1.5The HTML reporting library
53 | 54 | ## Design Pattern Used 55 | 56 | * Factory Pattern is used to reuse existing browser contexts instead of rebuilding them each time. 57 | * Bridge pattern is used to switch implementations between UI and API at runtime. 58 | * Decorator pattern is used to assign extra behaviors like highlight, retry .. to pages & elements at runtime without breaking the code that uses these objects. 59 | * Chain of Responsibility pattern is used to navigate from a pattern/page to other. 60 | * Observer pattern is used to switch to different data, browser on failure when retried. 61 | 62 | ## Framework - How to design your new test? 63 | 64 | * Step 1: Use the main source for the framework desaign. 65 | * Step 2: Refer the test source for the sample test code (Salesforce). 66 | * Step 3: Create your own app instance url and update the config. 67 | * Step 4: Build your pages like the sample using the right method and locator. 68 | * Step 5: Use the test data in your tests from faker or API or excel. 69 | * Step 6: Build your tests using the pages 70 | * Step 7: Once ready, run in the debug mode and look at the logs for details 71 | 72 | ## Amazing Usecases that you should try ! 73 | 74 |
✅ Single framework for both UI 🧭 and API Automation 75 | 90 |
91 | 92 |
✅ Fastest test execution 🚀 with configurable speed control 93 | 107 |
108 | 109 |
✅ Debug Faster with Snaps, Videos 🎥 and Tracings with configurable options 110 | 126 |
127 |
✅ Automated logins 💡 to avoid too many login tests 128 | 143 |
144 |
✅ Automated retries 🔁 with different browser and/or data 145 | 161 |
162 |
✅ Automated test data generation/selection for your CRUD operations 163 | 178 |
179 | 180 | ## Framework - How does it execute tests? 181 | 182 | * Step 01: You run your designed testng xml with listeners. 183 | * Step 02: The testng annotations (@Before) initialize the setup. 184 | * Step 03: The playwright script invokes the browser and actions. 185 | * Step 04: Simulatenously, the reporting engine captures the result. 186 | * Step 05: Upon success completion, the videos, reports are published. 187 | * Step 06: Upon failure, the testng retry is invoked with different data/browser. 188 | * Step 07: Step 02 - 06 continues until success or max retries. 189 | * Step 08: Look at the results folder for html results. 190 | * Step 09: Look at the videos for the exact details. 191 | * Step 10: Share the traceviewer results to your developer through defect log. 192 | 193 | ## 2023 Roadmap ! 194 | 195 | * Single framework to switch from Playwright to Selenium or vice versa. 196 | * Defect Management tools integration LIKE Jira and users choice. 197 | 198 | ## License 199 | 200 | The project is [MIT](./LICENSE) licensed. 201 | 202 | 203 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 4 | 4.0.0 5 | com.testleaf 6 | Salesforce 7 | 1.21.1 8 | simba 9 | Customized Playwright Framework for Web and API automation with scalability, exceptions handled and test reporting enabled. 10 | 11 | 12 | UTF-8 13 | UTF-8 14 | 3.0.0-M7 15 | 3.10.1 16 | 1.26.0 17 | 7.4.0 18 | 20220924 19 | 5.2.3 20 | 1.6.0 21 | 1.0.12 22 | 3.1.5 23 | local 24 | 25 | 26 | 27 | 28 | 29 | com.microsoft.playwright 30 | playwright 31 | ${playwright.version} 32 | 33 | 34 | 35 | org.testng 36 | testng 37 | ${testng.version} 38 | 39 | 40 | 41 | org.json 42 | json 43 | ${json.version} 44 | 45 | 46 | 47 | org.apache.poi 48 | poi 49 | ${poi.version} 50 | 51 | 52 | 53 | org.apache.poi 54 | poi-ooxml 55 | ${poi.version} 56 | 57 | 58 | 59 | net.datafaker 60 | datafaker 61 | ${datafaker.version} 62 | 63 | 64 | 65 | com.aventstack 66 | extentreports 67 | ${extent.version} 68 | 69 | 70 | 71 | org.aeonbits.owner 72 | owner 73 | ${owner.version} 74 | 75 | 76 | 77 | 78 | 79 | org.apache.maven.plugins 80 | maven-surefire-plugin 81 | ${maven-surefire-plugin.version} 82 | 83 | 84 | testng.xml 85 | 86 | 87 | 88 | 89 | org.apache.maven.plugins 90 | maven-compiler-plugin 91 | 3.1 92 | 93 | 1.8 94 | 1.8 95 | 96 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /src/main/java/com/force/base/DriverFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2022 TestLeaf 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.force.base; 26 | 27 | import com.force.config.ConfigurationManager; 28 | import com.google.gson.Gson; 29 | import com.google.gson.JsonElement; 30 | import com.microsoft.playwright.APIRequest; 31 | import com.microsoft.playwright.APIRequestContext; 32 | import com.microsoft.playwright.APIResponse; 33 | import com.microsoft.playwright.Browser; 34 | import com.microsoft.playwright.BrowserContext; 35 | import com.microsoft.playwright.BrowserType; 36 | import com.microsoft.playwright.FrameLocator; 37 | import com.microsoft.playwright.Page; 38 | import com.microsoft.playwright.Playwright; 39 | import com.microsoft.playwright.options.FormData; 40 | import com.microsoft.playwright.options.RequestOptions; 41 | 42 | public class DriverFactory { 43 | 44 | private static final ThreadLocal playwright = new ThreadLocal(); 45 | private static final ThreadLocal driver = new ThreadLocal(); 46 | private static final ThreadLocal token = new ThreadLocal(); 47 | protected static final ThreadLocal context = new ThreadLocal(); 48 | protected static final ThreadLocal page = new ThreadLocal(); 49 | protected static final ThreadLocal secondPage = new ThreadLocal(); 50 | protected static final ThreadLocal frameLocator = new ThreadLocal(); 51 | 52 | /** 53 | * Launches the preferred browser in the head(less) mode. 54 | * @param browser The accepted browsers are chrome, edge, firefox, safari (webkit) 55 | * @param headless Send true if you like to run in headless mode else false 56 | * @author TestLeaf 57 | */ 58 | 59 | public void setDriver(String browser, boolean headless) { 60 | System.setProperty("PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD", "1"); 61 | playwright.set(Playwright.create()); 62 | 63 | switch (browser) { 64 | case "chrome": 65 | driver.set(getPlaywright().chromium().launch( 66 | new BrowserType.LaunchOptions().setChannel("chrome") 67 | .setHeadless(headless) 68 | .setSlowMo(ConfigurationManager.configuration().slowMotion()))); 69 | break; 70 | case "edge": 71 | driver.set(getPlaywright().chromium().launch( 72 | new BrowserType.LaunchOptions().setChannel("msedge") 73 | .setHeadless(headless) 74 | .setSlowMo(ConfigurationManager.configuration().slowMotion()))); 75 | break; 76 | case "firefox": 77 | driver.set(getPlaywright().firefox().launch( 78 | new BrowserType.LaunchOptions() 79 | .setHeadless(headless) 80 | .setSlowMo(ConfigurationManager.configuration().slowMotion()))); 81 | case "safari": 82 | driver.set(getPlaywright().webkit().launch( 83 | new BrowserType.LaunchOptions() 84 | .setHeadless(headless) 85 | .setSlowMo(ConfigurationManager.configuration().slowMotion()))); 86 | default: 87 | break; 88 | } 89 | 90 | // Login through API and set the OAuth Token 91 | setOauthToken(); 92 | 93 | } 94 | 95 | public void setOauthToken() { 96 | 97 | // API request 98 | APIRequestContext request = getPlaywright().request().newContext(new APIRequest.NewContextOptions().setBaseURL(ConfigurationManager.configuration().oauthUrl())); 99 | 100 | // Request -> Post 101 | APIResponse response = request.post("", 102 | RequestOptions.create() 103 | .setForm(FormData.create() 104 | .set("grant_type", "password") 105 | .set("client_id", ConfigurationManager.configuration().clientId()) 106 | .set("client_secret", ConfigurationManager.configuration().clientSecret()) 107 | .set("username", ConfigurationManager.configuration().appUserName()) 108 | .set("password",ConfigurationManager.configuration().appPassword()) 109 | 110 | )); 111 | 112 | // Parse the json 113 | token.set(new Gson().fromJson(response.text(), JsonElement.class).getAsJsonObject().get("access_token").getAsString()); 114 | 115 | } 116 | 117 | public String getToken() { 118 | return token.get(); 119 | } 120 | 121 | public Browser getDriver() { 122 | return driver.get(); 123 | } 124 | 125 | public BrowserContext getContext() { 126 | return context.get(); 127 | } 128 | 129 | public Page getPage() { 130 | return page.get(); 131 | } 132 | 133 | public Page getSecondPage() { 134 | return secondPage.get(); 135 | } 136 | 137 | public FrameLocator getFrameLocator() { 138 | return frameLocator.get(); 139 | } 140 | 141 | public Playwright getPlaywright() { 142 | return playwright.get(); 143 | } 144 | 145 | } 146 | -------------------------------------------------------------------------------- /src/main/java/com/force/base/PlaywrightWrapper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2022 TestLeaf 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.force.base; 26 | 27 | import java.awt.GraphicsDevice; 28 | import java.awt.GraphicsEnvironment; 29 | import java.awt.HeadlessException; 30 | import java.nio.file.Paths; 31 | import java.util.Map; 32 | 33 | import com.force.config.ConfigurationManager; 34 | import com.force.utility.HtmlReporter; 35 | import com.google.gson.Gson; 36 | import com.google.gson.JsonElement; 37 | import com.microsoft.playwright.APIRequest; 38 | import com.microsoft.playwright.APIRequestContext; 39 | import com.microsoft.playwright.APIResponse; 40 | import com.microsoft.playwright.Locator; 41 | import com.microsoft.playwright.PlaywrightException; 42 | import com.microsoft.playwright.options.RequestOptions; 43 | import com.microsoft.playwright.options.SelectOption; 44 | import com.microsoft.playwright.options.WaitForSelectorState; 45 | 46 | public abstract class PlaywrightWrapper extends HtmlReporter{ 47 | 48 | int retry = 0; 49 | 50 | /** 51 | * Load the URL on the browser launched 52 | * @author TestLeaf 53 | * @param url The http(s) URL that to be loaded on the browser 54 | * @return true if the load is successful else false 55 | */ 56 | public boolean navigate(String url) { 57 | try { 58 | getPage().navigate(url); 59 | reportStep("The page with URL :"+url+" is loaded", "pass"); 60 | } catch (PlaywrightException e) { 61 | reportStep("PlaywrightException : \n" + e.getMessage(), "fail"); 62 | } 63 | return false; 64 | } 65 | 66 | /** 67 | * Maximize the browser based on screen size 68 | * Presently there is no built-in method to invoke. 69 | * @author TestLeaf 70 | */ 71 | public void maximize() { 72 | try { 73 | GraphicsDevice gd = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice(); 74 | getPage().setViewportSize(gd.getDisplayMode().getWidth(), gd.getDisplayMode().getHeight()); 75 | } catch (HeadlessException e) { 76 | 77 | } 78 | } 79 | 80 | /** 81 | * Check if the given selector of the element is visible or not after 2 seconds 82 | * @param locator The css / xpath / or playwright supported locator 83 | * @return true if the element is visible else false 84 | * @author TestLeaf 85 | */ 86 | public boolean isVisible(String locator) { 87 | boolean bVisible = false; 88 | try { 89 | getPage().setDefaultTimeout(2000); 90 | bVisible = getPage().isVisible(locator); 91 | } catch (PlaywrightException e) { 92 | } 93 | getPage().setDefaultTimeout(ConfigurationManager.configuration().timeout()); 94 | return bVisible; 95 | 96 | } 97 | 98 | /** 99 | * Use this method to typing into an element, which may set its value. 100 | * @param locator The locator to identify the element 101 | * @param value The value to be entered in the text 102 | * @param name The name of the text field (label) 103 | * @return true if the value is typed else false 104 | * @author TestLeaf 105 | */ 106 | public boolean type(String locator, String value, String name) { 107 | try { 108 | getPage().locator(locator).fill("");; 109 | getPage().locator(locator).type(value); 110 | reportStep("The text box :"+name+" is typed with value :"+value, "pass"); 111 | return true; 112 | } catch (PlaywrightException e) { 113 | e.printStackTrace(); 114 | reportStep("PlaywrightException : \n" + e.getMessage(), "fail"); 115 | } 116 | return false; 117 | } 118 | 119 | /** 120 | * Use this method to typing into an element inside a frame, which may set its value. 121 | * @param locator The locator to identify the element 122 | * @param value The value to be entered in the text 123 | * @param name The name of the text field (label) 124 | * @return true if the value is typed else false 125 | * @author TestLeaf 126 | */ 127 | public boolean typeInFrame(String locator, String value, String name) { 128 | try { 129 | getFrameLocator().locator(locator).fill("");; 130 | getFrameLocator().locator(locator).type(value); 131 | reportStep("The text box :"+name+" is typed with value :"+value, "pass"); 132 | return true; 133 | } catch (PlaywrightException e) { 134 | e.printStackTrace(); 135 | reportStep("PlaywrightException : \n" + e.getMessage(), "fail"); 136 | } 137 | return false; 138 | } 139 | 140 | /** 141 | * Use this method to typing into an element and perform key. 142 | * @param locator The locator to identify the element 143 | * @param value The value to be entered in the text 144 | * @param name The name of the text field (label) 145 | * @return true if the value is typed else false 146 | * @author TestLeaf 147 | */ 148 | public boolean typeAndEnter(String locator, String value, String name) { 149 | try { 150 | getPage().locator(locator).fill("");; 151 | getPage().locator(locator).type(value); 152 | getPage().locator(locator).press("Enter"); 153 | reportStep("The text box :"+name+" is typed with value :"+value, "pass"); 154 | return true; 155 | } catch (PlaywrightException e) { 156 | reportStep("PlaywrightException : \n" + e.getMessage(), "fail"); 157 | } 158 | return false; 159 | 160 | } 161 | 162 | /** 163 | * Use this method to upload a file into the chosen field. 164 | * @param locator The locator to identify the element where need to upload 165 | * @param fileName The file name (relative or absolute) 166 | * @param name The name of the upload field (label) 167 | * @return true if the file is uploaded else false 168 | * @author TestLeaf 169 | */ 170 | public boolean uploadFile(String locator, String fileName, String name) { 171 | try { 172 | getPage().locator(locator).setInputFiles(Paths.get(fileName)); 173 | reportStep("The text box :"+name+" is uploaded with file :"+fileName, "pass"); 174 | return true; 175 | } catch (PlaywrightException e) { 176 | reportStep("PlaywrightException : \n" + e.getMessage(), "fail"); 177 | } 178 | return false; 179 | 180 | } 181 | 182 | /** 183 | * Use this method to click a button. 184 | * @param locator The locator to identify the element 185 | * @param name The name of the button field (label) 186 | * @return true if the button is clicked else false 187 | * @author TestLeaf 188 | */ 189 | public boolean click(String locator, String name) { 190 | return click(locator, name, "button"); 191 | } 192 | 193 | /** 194 | * Use this method to click an element. 195 | * @param locator The locator to identify the element 196 | * @param name The name of the element field (label) 197 | * @param type The type of the element such as link, element etc 198 | * @return true if the element is clicked else false 199 | * @author TestLeaf 200 | */ 201 | public boolean click(String locator, String name, String type) { 202 | try { 203 | getPage().locator(locator).scrollIntoViewIfNeeded(); 204 | getPage().locator(locator).click(); 205 | reportStep("The "+type+" :"+name+" is clicked", "pass"); 206 | return true; 207 | } catch (PlaywrightException e) { 208 | reportStep("PlaywrightException : \n" + e.getMessage(), "fail"); 209 | } 210 | return false; 211 | 212 | } 213 | 214 | /** 215 | * Use this method to click an element within a frame. 216 | * @param locator The locator to identify the element 217 | * @param name The name of the element field (label) 218 | * @param type The type of the element such as link, element etc 219 | * @return true if the element is clicked else false 220 | * @author TestLeaf 221 | */ 222 | public boolean clickInFrame(String locator, String name) { 223 | try { 224 | getFrameLocator().locator(locator).scrollIntoViewIfNeeded(); 225 | getFrameLocator().locator(locator).click(); 226 | reportStep("The button :"+name+" is clicked", "pass"); 227 | return true; 228 | } catch (PlaywrightException e) { 229 | e.printStackTrace(); 230 | reportStep("PlaywrightException : \n" + e.getMessage(), "fail"); 231 | } 232 | return false; 233 | } 234 | 235 | /** 236 | * Use this method to check a checkbox. 237 | * @param locator The locator to identify the checkbox 238 | * @param name The name of the checkbox field (label) 239 | * @return true if the checkbox is checked else false 240 | * @author TestLeaf 241 | */ 242 | public boolean check(String locator, String name) { 243 | try { 244 | getPage().locator(locator).check(); 245 | reportStep("The checkbox: "+name+" is checked", "pass"); 246 | return true; 247 | } catch (PlaywrightException e) { 248 | reportStep("PlaywrightException : \n" + e.getMessage(), "fail"); 249 | } 250 | return false; 251 | } 252 | 253 | /** 254 | * Use this method to select a dropdown by its text. 255 | * @param locator The locator to identify the dropdown 256 | * @param text The text to be selected in the dropdown 257 | * @param name The name of the dropdown field (label) 258 | * @return true if the dropdown is selected else false 259 | * @author TestLeaf 260 | */ 261 | public boolean selectByText(String locator, String text, String name) { 262 | try { 263 | getPage().locator(locator).selectOption(new SelectOption().setLabel(text)); 264 | reportStep("The drop down :"+name+" is selected with value :"+text, "pass"); 265 | return true; 266 | } catch (PlaywrightException e) { 267 | e.getMessage(); 268 | reportStep("PlaywrightException : \n" + e.getMessage(), "fail"); 269 | } 270 | return false; 271 | 272 | } 273 | 274 | /** 275 | * Use this method to select a dropdown by its value. 276 | * @param locator The locator to identify the dropdown 277 | * @param value The value based on which it to be selected in the dropdown 278 | * @param name The name of the dropdown field (label) 279 | * @return true if the dropdown is selected else false 280 | * @author TestLeaf 281 | */ 282 | public boolean selectByValue(String locator, String value, String name) { 283 | try { 284 | getPage().locator(locator).selectOption(value); 285 | reportStep("The drop down :"+name+" is selected with value index as :"+value, "pass"); 286 | return true; 287 | } catch (PlaywrightException e) { 288 | e.printStackTrace(); 289 | reportStep("PlaywrightException : \n" + e.getMessage(), "fail"); 290 | } 291 | return false; 292 | 293 | } 294 | 295 | /** 296 | * Use this method to select a dropdown by its index. 297 | * @param locator The locator to identify the dropdown 298 | * @param index The index to be selected in the dropdown (starts at 0) 299 | * @param name The name of the dropdown field (label) 300 | * @return true if the dropdown is selected else false 301 | * @author TestLeaf 302 | */ 303 | public boolean selectByIndex(String locatorId,int index, String name) { 304 | try { 305 | Locator locator = getPage().locator(locatorId+" > option"); 306 | if(index > locator.count() || index < 0) index = (int)Math.floor(Math.random()*(locator.count()-1))+1; 307 | getPage().locator(locatorId).selectOption(locator.nth(index).getAttribute("value")); 308 | reportStep("The drop down :"+name+" is selected with index :"+index, "pass"); 309 | return true; 310 | } catch (PlaywrightException e) { 311 | e.getMessage(); 312 | reportStep("PlaywrightException : \n" + e.getMessage(), "fail"); 313 | } 314 | return false; 315 | 316 | } 317 | 318 | /** 319 | * Use this method to select a dropdown by its index inside a frame. 320 | * @param locator The locator to identify the dropdown 321 | * @param index The index to be selected in the dropdown (starts at 0) 322 | * @param name The name of the dropdown field (label) 323 | * @return true if the dropdown is selected else false 324 | * @author TestLeaf 325 | */ 326 | public boolean selectByIndexInFrame(String locatorId,int index, String name) { 327 | try { 328 | Locator locator = getFrameLocator().locator(locatorId+" > option"); 329 | if(index > locator.count() || index < 0) index = (int)Math.floor(Math.random()*(locator.count()-1))+1; 330 | getFrameLocator().locator(locatorId).selectOption(locator.nth(index).getAttribute("value")); 331 | reportStep("The drop down :"+name+" is selected with index :"+index, "pass"); 332 | return true; 333 | } catch (PlaywrightException e) { 334 | e.getMessage(); 335 | reportStep("PlaywrightException : \n" + e.getMessage(), "fail"); 336 | } 337 | return false; 338 | 339 | } 340 | 341 | /** 342 | * Use this method to select a dropdown by the random index. 343 | * @param locator The locator to identify the dropdown 344 | * @param name The name of the dropdown field (label) 345 | * @return true if the dropdown is selected else false 346 | * @author TestLeaf 347 | */ 348 | public boolean selectByRandomIndex(String locator,String name) { 349 | return selectByIndex(locator, -1, name); 350 | } 351 | 352 | /** 353 | * Use this method to type and choose an element (that looks like dropdown) 354 | * @param ddLocator The Dropdown locator to identify the main select element 355 | * @param optionLocator The Option locator to identify the item element 356 | * @param option The option to be entered in the text area 357 | * @param name The name of the dropdown field (label) 358 | * @return true if the option is selected else false 359 | * @author TestLeaf 360 | */ 361 | public boolean clickAndType(String ddLocator, String optionLocator, String option, String name) { 362 | try { 363 | getPage().locator(ddLocator).click(); 364 | getPage().locator(optionLocator).type(option); 365 | getPage().keyboard().press("Enter"); 366 | reportStep("The drop down :"+name+" is selected with value :"+option, "pass"); 367 | return true; 368 | } catch (PlaywrightException e) { 369 | reportStep("PlaywrightException : \n" + e.getMessage(), "fail"); 370 | } 371 | return false; 372 | } 373 | 374 | /** 375 | * Use this method to click and choose an element (that looks like dropdown) 376 | * @param ddLocator The Dropdown locator to identify the main select element 377 | * @param optionLocator The Option locator to identify the item element 378 | * @param option The option to be selected in the dropdown 379 | * @param name The name of the dropdown field (label) 380 | * @return true if the option is selected else false 381 | * @author TestLeaf 382 | */ 383 | public boolean clickAndChoose(String ddLocator, String optionLocator, String option, String name) { 384 | try { 385 | getPage().locator(ddLocator).click(); 386 | getPage().locator(optionLocator).click(); 387 | reportStep("The drop down :"+name+" is selected with value :"+option, "pass"); 388 | return true; 389 | } catch (PlaywrightException e) { 390 | e.printStackTrace(); 391 | reportStep("PlaywrightException : \n" + e.getMessage(), "fail"); 392 | } 393 | return false; 394 | } 395 | 396 | /** 397 | * Use this method to click and choose an element (that looks like dropdown) inside a frame 398 | * @param ddLocator The Dropdown locator to identify the main select element 399 | * @param optionLocator The Option locator to identify the item element 400 | * @param option The option to be selected in the dropdown 401 | * @param name The name of the dropdown field (label) 402 | * @return true if the option is selected else false 403 | * @author TestLeaf 404 | */ 405 | public boolean clickAndChooseInFrame(String ddLocator, String optionLocator, String option, String name) { 406 | try { 407 | getFrameLocator().locator(ddLocator).click(); 408 | getFrameLocator().locator(optionLocator).click(); 409 | reportStep("The drop down :"+name+" is selected with value :"+option, "pass"); 410 | return true; 411 | } catch (PlaywrightException e) { 412 | e.printStackTrace(); 413 | reportStep("PlaywrightException : \n" + e.getMessage(), "fail"); 414 | } 415 | return false; 416 | } 417 | 418 | /** 419 | * Use this method to mouse over an element 420 | * @param locator The locator to identify the element 421 | * @param name The name of the element (label) 422 | * @return true if the mouse over is done else false 423 | * @author TestLeaf 424 | */ 425 | public boolean mouseOver(String locator, String name) { 426 | try { 427 | getPage().locator(locator).hover(); 428 | reportStep("The element :"+name+" is moused over successfully", "pass"); 429 | return true; 430 | } catch (PlaywrightException e) { 431 | e.printStackTrace(); 432 | reportStep("PlaywrightException : \n" + e.getMessage(), "fail"); 433 | } 434 | return false; 435 | } 436 | 437 | /** 438 | * Use this method to get inner text of an element 439 | * @param locator The locator to identify the element 440 | * @param name The name of the element (label) 441 | * @return true if the mouse over is done else false 442 | * @author TestLeaf 443 | */ 444 | public String getInnerText(String locator) { 445 | try { 446 | return getPage().locator(locator).innerText(); 447 | } catch(Exception e) {} 448 | return ""; 449 | } 450 | 451 | /** 452 | * Use this method to check if the element is enabled 453 | * @param locator The locator to identify the element 454 | * @param name The name of the element (label) 455 | * @return true if the element is enabled else false 456 | * @author TestLeaf 457 | */ 458 | public boolean isEnabled(String locator) { 459 | try { 460 | return getPage().locator(locator).isEnabled(); 461 | } catch(Exception e) {} 462 | return false; 463 | } 464 | 465 | /** 466 | * Use this method to check if the element is disabled 467 | * @param locator The locator to identify the element 468 | * @param name The name of the element (label) 469 | * @return true if the element is disabled else false 470 | * @author TestLeaf 471 | */ 472 | public boolean isDisabled(String locator) { 473 | try { 474 | return getPage().locator(locator).isDisabled(); 475 | } catch(Exception e) {} 476 | return false; 477 | } 478 | 479 | /** 480 | * Use this method to report if the element is disabled 481 | * @param locator The locator to identify the element 482 | * @param name The name of the element (label) 483 | * @return true if the element is disabled else false 484 | * @author TestLeaf 485 | */ 486 | public boolean verifyDisabled(String locator, String label) { 487 | try { 488 | if(isDisabled(locator)) reportStep("The element :"+label+" is disabled as expected", "pass"); 489 | else reportStep("The element :"+label+" is enabled", "warning"); 490 | } catch(Exception e) {} 491 | return false; 492 | } 493 | 494 | /** 495 | * Use this method to wait for the element to disappear 496 | * @param locator The locator to identify the element 497 | * @return true if the element is disappeared else false 498 | * @author TestLeaf 499 | */ 500 | public boolean waitForDisappearance(String locator) { 501 | try { 502 | getPage().locator(locator).waitFor(new Locator.WaitForOptions().setTimeout(ConfigurationManager.configuration().timeout()).setState(WaitForSelectorState.HIDDEN)); 503 | return true; 504 | } catch(Exception e) {} 505 | return false; 506 | } 507 | 508 | /** 509 | * Use this method to wait for the element to appear 510 | * @param locator The locator to identify the element 511 | * @return true if the element is appeared else false 512 | * @author TestLeaf 513 | */ 514 | public boolean waitForAppearance(String locator) { 515 | try { 516 | getPage().locator(locator).waitFor(new Locator.WaitForOptions().setTimeout(ConfigurationManager.configuration().timeout()).setState(WaitForSelectorState.VISIBLE)); 517 | return true; 518 | } catch(Exception e) {} 519 | return false; 520 | } 521 | 522 | /** 523 | * Use this method to report the correctness of the title 524 | * @param title The title of the browser 525 | * @return true if the title matches the partial content else false 526 | * @author TestLeaf 527 | */ 528 | public boolean verifyTitle(String title) { 529 | try { 530 | if(getPage().title().contains(title)) { 531 | reportStep("The page with title :"+title+" displayed as expected", "pass"); 532 | return true; 533 | }else 534 | reportStep("The page with title :"+title+" did not match", "warning"); 535 | 536 | } catch (PlaywrightException e) { 537 | reportStep("PlaywrightException : \n" + e.getMessage(), "fail"); 538 | } 539 | return false; 540 | 541 | } 542 | 543 | /** 544 | * Use this method to report the correctness of the inner text (Exact Match) 545 | * @param locator The locator to identify the element 546 | * @param expectedText The expected text to be verified 547 | * @return true if the inner text matches the exact content else false 548 | * @author TestLeaf 549 | */ 550 | public boolean verifyExactText(String locator, String expectedText) { 551 | try { 552 | System.out.println(getPage().innerText(locator)); 553 | if(getPage().locator(locator).innerText().equals(expectedText)) { 554 | reportStep("The element with text :"+expectedText+" displayed as expected", "pass"); 555 | return true; 556 | }else 557 | reportStep("The element with text :"+expectedText+" did not match", "warning"); 558 | 559 | } catch (PlaywrightException e) { 560 | reportStep("PlaywrightException : \n" + e.getMessage(), "fail"); 561 | } 562 | return false; 563 | 564 | } 565 | 566 | /** 567 | * Use this method to report the correctness of the inner text (Partial Match) 568 | * @param locator The locator to identify the element 569 | * @param expectedText The expected text to be verified 570 | * @return true if the inner text matches the partial content else false 571 | * @author TestLeaf 572 | */ 573 | public boolean verifyPartialText(String locator, String expectedText) { 574 | try { 575 | if(getPage().locator(locator).innerText().contains(expectedText)) { 576 | reportStep("The element with text :"+expectedText+" displayed as expected", "pass"); 577 | return true; 578 | }else 579 | reportStep("The element with text :"+expectedText+" did not match", "warning"); 580 | 581 | } catch (PlaywrightException e) { 582 | reportStep("PlaywrightException : \n" + e.getMessage(), "fail"); 583 | } 584 | return false; 585 | 586 | } 587 | 588 | /** 589 | * Use this method return the text typed within a textbox/area 590 | * @param locator The locator to identify the element 591 | * @return Returns the text typed 592 | * @author TestLeaf 593 | */ 594 | public String getInputText(String locator) { 595 | try { 596 | return getPage().locator(locator).inputValue(); 597 | } catch (PlaywrightException e) {} 598 | return ""; 599 | } 600 | 601 | 602 | /** 603 | * Use this method to report the correctness of the typed text (Partial Match) 604 | * @param locator The locator to identify the element 605 | * @param expectedText The expected text to be verified 606 | * @return true if the typed text matches the partial content else false 607 | * @author TestLeaf 608 | */ 609 | public boolean verifyInputText(String locator,String expectedText) { 610 | try { 611 | if(getPage().locator(locator).inputValue().contains(expectedText)) { 612 | reportStep("The element with text :"+expectedText+" displayed as expected", "pass"); 613 | return true; 614 | }else 615 | reportStep("The element with text :"+expectedText+" did not match", "warning"); 616 | 617 | } catch (PlaywrightException e) { 618 | reportStep("PlaywrightException : \n" + e.getMessage(), "fail"); 619 | } 620 | return false; 621 | 622 | } 623 | 624 | /** 625 | * Use this method to get the attribute of the element 626 | * @param locator The locator to identify the element 627 | * @param attribute The attribute of the element 628 | * @return The attribute value of the located element 629 | * @author TestLeaf 630 | */ 631 | public String getAttribute(String locator, String attribute) { 632 | try { 633 | return getPage().locator(locator).getAttribute(attribute); 634 | } catch (PlaywrightException e) {} 635 | return ""; 636 | } 637 | 638 | /** 639 | * Use this method to verify attribute of the element (Partial Match) 640 | * @param locator The locator to identify the element 641 | * @param attribute The attribute of the element 642 | * @param expectedText The expected attribute value of the located element 643 | * @return true if the attribute matches the partial content else false 644 | * @author TestLeaf 645 | */ 646 | public boolean verifyAttribute(String locator, String attribute, String expectedText) { 647 | try { 648 | if(getPage().locator(locator).getAttribute(attribute).contains(expectedText)) { 649 | reportStep("The element with text :"+expectedText+" displayed as expected", "pass"); 650 | return true; 651 | }else 652 | reportStep("The element with text :"+expectedText+" did not match", "warning"); 653 | 654 | } catch (PlaywrightException e) { 655 | reportStep("PlaywrightException : \n" + e.getMessage(), "fail"); 656 | } 657 | return false; 658 | } 659 | 660 | /** 661 | * Use this method to verify attribute of the element (Partial Match) inside a frame 662 | * @param locator The locator to identify the element 663 | * @param attribute The attribute of the element 664 | * @param expectedText The expected attribute value of the located element 665 | * @return true if the attribute matches the partial content else false 666 | * @author TestLeaf 667 | */ 668 | public boolean verifyAttributeInFrame(String locator, String attribute, String expectedText) { 669 | try { 670 | if(getFrameLocator().locator(locator).getAttribute(attribute).contains(expectedText)) { 671 | reportStep("The element with text :"+expectedText+" displayed as expected", "pass"); 672 | return true; 673 | }else 674 | reportStep("The element with text :"+expectedText+" did not match", "warning"); 675 | 676 | } catch (PlaywrightException e) { 677 | reportStep("PlaywrightException : \n" + e.getMessage(), "fail"); 678 | } 679 | return false; 680 | 681 | } 682 | 683 | private boolean reportVisibleSuccess(String locator, String name) { 684 | getPage().locator(locator).scrollIntoViewIfNeeded(); 685 | reportStep("The element :"+name+" displayed as expected", "pass"); 686 | return true; 687 | } 688 | 689 | /** 690 | * Use this method to verify visibility of the element 691 | * @param locator The locator to identify the element 692 | * @param name The name of the element field (label) 693 | * @return true if the element visible else false 694 | * @author TestLeaf 695 | */ 696 | public boolean verifyDisplayed(String locator, String name) { 697 | try { 698 | if(getPage().locator(locator).isVisible()) { 699 | return reportVisibleSuccess(locator, name); 700 | } else { 701 | pause("medium"); 702 | if(getPage().isVisible(locator)) { 703 | return reportVisibleSuccess(locator, name); 704 | } 705 | } 706 | } catch (PlaywrightException e) { 707 | reportStep("PlaywrightException : \n" + e.getMessage(), "fail"); 708 | } 709 | if(!getPage().isVisible(locator)) 710 | reportStep("The element :"+name+" is not visible in the page", "warning"); 711 | return false; 712 | } 713 | 714 | /** 715 | * Use this method to verify invisibility of the element 716 | * @param locator The locator to identify the element 717 | * @param name The name of the element field (label) 718 | * @return true if the element invisible else false 719 | * @author TestLeaf 720 | */ 721 | public boolean verifyNotDisplayed(String locator, String name) { 722 | try { 723 | if(!getPage().locator(locator).isVisible()) { 724 | reportStep("The element :"+name+" is not displayed as expected", "pass"); 725 | return true; 726 | } else { 727 | pause("medium"); 728 | reportStep("The element :"+name+" is not displayed as expected", "pass"); 729 | return true; 730 | } 731 | } catch (PlaywrightException e) { 732 | reportStep("PlaywrightException : \n" + e.getMessage(), "fail"); 733 | } 734 | if(!getPage().locator(locator).isVisible()) 735 | reportStep("The element :"+name+" is visible in the page", "warning"); 736 | return false; 737 | } 738 | 739 | /** 740 | * Use this method to manage the wait between actions with sleep 741 | * @param type The type of wait - allowed : low, medium, high 742 | * @author TestLeaf 743 | */ 744 | protected void pause(String type) { 745 | try { 746 | switch (type.toLowerCase()) { 747 | case "low": 748 | Thread.sleep(ConfigurationManager.configuration().pauseLow()); 749 | break; 750 | case "medium": 751 | Thread.sleep(ConfigurationManager.configuration().pauseMedium()); 752 | break; 753 | case "high": 754 | Thread.sleep(ConfigurationManager.configuration().pauseHigh()); 755 | break; 756 | default: 757 | Thread.sleep(ConfigurationManager.configuration().pauseLow()); 758 | break; 759 | } 760 | } catch (Exception e) { } 761 | } 762 | 763 | 764 | public JsonElement getRequest(String resource, Map headers) { 765 | 766 | // Send API request --> base end point url 767 | APIRequestContext request = getPlaywright().request().newContext( 768 | new APIRequest.NewContextOptions() 769 | .setBaseURL(ConfigurationManager.configuration().apiUrl()) 770 | .setExtraHTTPHeaders(headers)); 771 | 772 | // request -> Get 773 | APIResponse response = request.get(resource); 774 | 775 | // Parse response 776 | return new Gson().fromJson(response.text(), JsonElement.class); 777 | 778 | } 779 | 780 | public JsonElement postRequest(String resource, Map headers, String jsonBody) { 781 | 782 | // Send API request --> base end point url 783 | APIRequestContext request = getPlaywright().request().newContext( 784 | new APIRequest.NewContextOptions() 785 | .setBaseURL(ConfigurationManager.configuration().apiUrl()) 786 | .setExtraHTTPHeaders(headers)); 787 | 788 | // request -> Post 789 | APIResponse response = request.post(resource, RequestOptions.create().setData(jsonBody)); 790 | System.out.println(response.text()); 791 | // Parse response 792 | return new Gson().fromJson(response.text(), JsonElement.class); 793 | 794 | } 795 | 796 | } 797 | -------------------------------------------------------------------------------- /src/main/java/com/force/base/ProjectHooks.java: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2022 TestLeaf 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.force.base; 26 | 27 | import java.io.IOException; 28 | import java.nio.file.Paths; 29 | import java.util.Base64; 30 | import java.util.HashMap; 31 | import java.util.Map; 32 | import org.testng.annotations.AfterMethod; 33 | import org.testng.annotations.BeforeClass; 34 | import org.testng.annotations.BeforeMethod; 35 | import org.testng.annotations.BeforeSuite; 36 | import org.testng.annotations.DataProvider; 37 | import com.force.config.ConfigurationManager; 38 | import com.force.utility.DataLibrary; 39 | import com.microsoft.playwright.Browser; 40 | import com.microsoft.playwright.Tracing; 41 | import com.microsoft.playwright.Browser.NewContextOptions; 42 | import com.microsoft.playwright.Video; 43 | 44 | public class ProjectHooks extends PlaywrightWrapper { 45 | 46 | // This is for the email that you may need to use within test automation 47 | protected static final ThreadLocal email = new ThreadLocal(); 48 | 49 | public static Map newEmails; 50 | public static String videoFolderName = "videos/"; 51 | public static String tracesFolderName = "videos/"; 52 | 53 | 54 | /** 55 | * Will be invoked before once for every test suite execution and create video folder and the reporting 56 | * @author TestLeaf 57 | */ 58 | @BeforeSuite 59 | public void initSuite() { 60 | newEmails = new HashMap(); 61 | videoFolderName = createFolder("videos"); 62 | tracesFolderName = createFolder("traces"); 63 | 64 | startReport(); 65 | } 66 | 67 | /** 68 | * This method will take snapshot in base64 format 69 | * @author TestLeaf 70 | */ 71 | @Override 72 | public String takeSnap(){ 73 | return new String(Base64.getEncoder().encode(getPage().screenshot())); 74 | } 75 | 76 | /** 77 | * Will be invoked before once for every test case execution and 78 | * a) will launch the browser based on config 79 | * b) create reporting structure 80 | * c) store login state (if configured) 81 | * d) create context, page 82 | * e) set default time out based on config 83 | * f) maximize and load the given URL 84 | * @author TestLeaf 85 | */ 86 | @BeforeMethod 87 | public void init() { 88 | try { 89 | // Launch the browser (based on configuration) in head(less) mode (based on configuration) 90 | setDriver(ConfigurationManager.configuration().browser(), ConfigurationManager.configuration().headless()); 91 | 92 | // Set the extent report node for the test 93 | setNode(); 94 | 95 | // Default Settings 96 | NewContextOptions newContext = new Browser.NewContextOptions() 97 | .setIgnoreHTTPSErrors(true) 98 | .setRecordVideoDir(Paths.get(folderName)); 99 | 100 | // Auto Login if enabled 101 | if(ConfigurationManager.configuration().autoLogin()) { 102 | newContext.setStorageStatePath(Paths.get("storage/login.json")); 103 | } 104 | 105 | // Store for Auto Login, Set the video recording ON using context 106 | context.set(getDriver().newContext(newContext)); 107 | 108 | // Create a new page and assign to the thread local 109 | page.set(getContext().newPage()); 110 | 111 | // Set the timeout based on the configuration 112 | getPage().setDefaultTimeout(ConfigurationManager.configuration().timeout()); 113 | 114 | // enable Tracing 115 | if(ConfigurationManager.configuration().enableTracing()) 116 | getContext().tracing().start(new Tracing.StartOptions().setName(testcaseName).setSnapshots(true).setTitle(testcaseName)); 117 | 118 | // Get the screen size and maximize 119 | maximize(); 120 | 121 | // Load the page with URL based on configuration 122 | navigate(ConfigurationManager.configuration().baseUrl()); 123 | 124 | } catch (Exception e) { 125 | reportStep("The browser and/or the URL could not be loaded as expected", "fail"); 126 | } 127 | } 128 | 129 | @BeforeClass(alwaysRun = true) 130 | public void startTestcaseReporting() { 131 | startTestCase(); 132 | } 133 | 134 | /** 135 | * Will be invoked after once for every test case execution and 136 | * a) video & tracing will be created in the given folder 137 | * b) result will be published 138 | * @author TestLeaf 139 | */ 140 | @AfterMethod(alwaysRun = true) 141 | public void tearDown() { 142 | try { 143 | 144 | // End tracing 145 | getContext().tracing().stop(new Tracing.StopOptions().setPath(Paths.get(tracesFolderName+"/"+testcaseName+".zip"))); 146 | Video video = getPage().video(); 147 | getPage().close(); 148 | video.saveAs(Paths.get(videoFolderName+"/"+testcaseName+".webm")); 149 | getContext().close(); // video will be saved 150 | video.delete(); 151 | getPlaywright().close(); 152 | endResult(); 153 | } catch (Exception e) { 154 | e.printStackTrace(); 155 | } 156 | } 157 | 158 | @DataProvider 159 | public Object[][] getData() throws IOException { 160 | return DataLibrary.readExcelData(dataFileName); 161 | } 162 | 163 | 164 | 165 | } 166 | -------------------------------------------------------------------------------- /src/main/java/com/force/config/BrowserConfig.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Apache 2.0 License 3 | * 4 | * Copyright (c) 2022 TestLeaf 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.force.config; 26 | 27 | public interface BrowserConfig{ 28 | 29 | public static final String START_MAXIMIZED = "--start-maximized"; 30 | public static final String DISABLE_INFOBARS = "--disable-infobars"; 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/force/config/Configuration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Apache 2.0 License 3 | * 4 | * Copyright (c) 2022 TestLeaf 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.force.config; 26 | 27 | import org.aeonbits.owner.Config; 28 | import org.aeonbits.owner.Config.LoadPolicy; 29 | import org.aeonbits.owner.Config.LoadType; 30 | 31 | @LoadPolicy(LoadType.MERGE) 32 | @Config.Sources({ 33 | "system:properties", 34 | "classpath:local.properties", 35 | "classpath:app.properties", 36 | "classpath:grid.properties", 37 | "classpath:report.properties"}) 38 | public interface Configuration extends Config { 39 | 40 | @Key("target") 41 | String target(); 42 | 43 | @Key("browser") 44 | String browser(); 45 | 46 | @Key("headless") 47 | Boolean headless(); 48 | 49 | @Key("url.api") 50 | String apiUrl(); 51 | 52 | @Key("url.base") 53 | String baseUrl(); 54 | 55 | @Key("client.id") 56 | String clientId(); 57 | 58 | @Key("client.secret") 59 | String clientSecret(); 60 | 61 | @Key("url.oauth") 62 | String oauthUrl(); 63 | 64 | @Key("timeout") 65 | int timeout(); 66 | 67 | @Key("grid.url") 68 | String gridUrl(); 69 | 70 | @Key("grid.port") 71 | String gridPort(); 72 | 73 | @Key("faker.locale") 74 | String faker(); 75 | 76 | @Key("auto.login") 77 | boolean autoLogin(); 78 | 79 | @Key("enable.tracing") 80 | boolean enableTracing(); 81 | 82 | @Key("action.delay") 83 | double slowMotion(); 84 | 85 | @Key("pause.low") 86 | long pauseLow(); 87 | 88 | @Key("pause.medium") 89 | long pauseMedium(); 90 | 91 | @Key("pause.high") 92 | long pauseHigh(); 93 | 94 | @Key("email.max.timeout") 95 | int maxEmailTimeout(); 96 | 97 | @Key("max.retry") 98 | int maxRetry(); 99 | 100 | @Key("app.username") 101 | String appUserName(); 102 | 103 | @Key("app.password") 104 | String appPassword(); 105 | 106 | @Key("report.title") 107 | String reportTitle(); 108 | 109 | @Key("report.name") 110 | String reportName(); 111 | 112 | @Key("report.theme") 113 | String reportTheme(); 114 | 115 | 116 | } 117 | -------------------------------------------------------------------------------- /src/main/java/com/force/config/ConfigurationManager.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Apache 2.0 License 3 | * 4 | * Copyright (c) 2022 TestLeaf 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.force.config; 26 | 27 | import org.aeonbits.owner.ConfigCache; 28 | 29 | public class ConfigurationManager { 30 | 31 | private ConfigurationManager() { 32 | } 33 | 34 | public static Configuration configuration() { 35 | return ConfigCache.getOrCreate(Configuration.class); 36 | } 37 | } -------------------------------------------------------------------------------- /src/main/java/com/force/data/dynamic/FakerDataFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2022 TestLeaf 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.force.data.dynamic; 26 | 27 | import net.datafaker.Faker; 28 | import java.util.Locale; 29 | import com.force.enums.Country; 30 | 31 | import static com.force.config.ConfigurationManager.configuration; 32 | 33 | public class FakerDataFactory { 34 | 35 | private static final Faker faker = new Faker(new Locale(configuration().faker())); 36 | 37 | private FakerDataFactory() { 38 | 39 | } 40 | 41 | public static String getCompanyName() { 42 | return faker.company().name().replaceAll("[^a-zA-Z ]", ""); 43 | } 44 | 45 | public static String getUrl() { 46 | return faker.company().url(); 47 | } 48 | 49 | public static String getAddress() { 50 | return faker.address().streetAddress(); 51 | } 52 | 53 | public static String getCity() { 54 | return faker.address().city(); 55 | } 56 | 57 | public static String getCountry() { 58 | return Country.getRandom().get().toString(); 59 | } 60 | 61 | public static String getFirstName() { 62 | return faker.name().firstName(); 63 | } 64 | 65 | public static String getLastName() { 66 | return faker.name().lastName(); 67 | } 68 | 69 | public static String getEmailAddress() { 70 | return faker.internet().emailAddress(); 71 | } 72 | 73 | public static String getContactNumber() { 74 | return faker.phoneNumber().cellPhone(); 75 | } 76 | 77 | public static String getBankAccountNumber() { 78 | return ""+faker.number().randomNumber(8, false); 79 | } 80 | 81 | public static String getSwiftCode() { 82 | return ""+faker.number().randomNumber(4, false); 83 | } 84 | public static String getTaxNumber() { 85 | return ""+faker.number().randomNumber(7, false); 86 | } 87 | 88 | public static String getRating() { 89 | return faker.options().option("Hot","Warm", "Cold"); 90 | } 91 | 92 | public static String getSalutation() { 93 | return faker.options().option("Mr.","Ms.", "Mrs.", "Dr.", "Prof."); 94 | } 95 | 96 | 97 | 98 | } 99 | -------------------------------------------------------------------------------- /src/main/java/com/force/enums/Country.java: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2022 TestLeaf 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.force.enums; 26 | 27 | import java.security.SecureRandom; 28 | import java.util.function.Supplier; 29 | 30 | public enum Country implements Supplier { 31 | 32 | USA("United States"), 33 | ALBANIA("Albania"), 34 | ALGERIA("Algeria"), 35 | ARGENTINA("Argentina"), 36 | ARMENIA("Armenia"), 37 | AUSTRIA("Austria"), 38 | BANGLADESH("Bangladesh"), 39 | BRAZIL("Brazil"), 40 | CANADA("Canada"), 41 | CHILE("Chile"), 42 | CHINA("China"), 43 | CUBA("Cuba"), 44 | EGYPT("Egypt"), 45 | FRANCE("France"), 46 | INDIA("India"), 47 | IRAN("Iran"), 48 | IRAQ("Iraq"), 49 | KENYA("Kenya"), 50 | NEPAL("Nepal"), 51 | PORTUGAL("Portugal"), 52 | SWEDEN("Sweden"), 53 | UNITED_ARAB_EMIRATES("United Arab Emirates"), 54 | UK("UK"), 55 | ZIMBABWE("Zimbabwe"); 56 | /* 57 | USA("United States of America"), 58 | AFGHANISTAN("Afghanistan"), 59 | ALBANIA("Albania"), 60 | ALGERIA("Algeria"), 61 | ANDORRA("Andorra"), 62 | ANGOLA("Angola"), 63 | ANTIGUA_DEPS("Antigua & Deps"), 64 | ARGENTINA("Argentina"), 65 | ARMENIA("Armenia"), 66 | AUSTRALIA("Australia"), 67 | AUSTRIA("Austria"), 68 | AZERBAIJAN("Azerbaijan"), 69 | BAHAMAS("Bahamas"), 70 | BAHRAIN("Bahrain"), 71 | BANGLADESH("Bangladesh"), 72 | BARBADOS("Barbados"), 73 | BELARUS("Belarus"), 74 | BELGIUM("Belgium"), 75 | BELIZE("Belize"), 76 | BENIN("Benin"), 77 | BHUTAN("Bhutan"), 78 | BOLIVIA("Bolivia"), 79 | BOSNIA_HERZEGOVINA("Bosnia Herzegovina"), 80 | BOTSWANA("Botswana"), 81 | BRAZIL("Brazil"), 82 | BRUNEI("Brunei"), 83 | BULGARIA("Bulgaria"), 84 | BURKINA("Burkina"), 85 | BURMA("Burma"), 86 | BURUNDI("Burundi"), 87 | CAMBODIA("Cambodia"), 88 | CAMEROON("Cameroon"), 89 | CANADA("Canada"), 90 | CAPE_VERDE("Cape Verde"), 91 | CENTRAL_AFRICAN_REP("Central African Rep"), 92 | CHAD("Chad"), 93 | CHILE("Chile"), 94 | CHINA("Republic of China"), 95 | REPUBLIC_OF_CHINA("Republic of China"), 96 | COLOMBIA("Colombia"), 97 | COMOROS("Comoros"), 98 | DEMOCRATIC_REPUBLIC_OF_THE_CONGO("Democratic Republic of the Congo"), 99 | REPUBLIC_OF_THE_CONGO("Republic of the Congo"), 100 | COSTA_RICA("Costa Rica"), 101 | CROATIA("Croatia"), 102 | CUBA("Cuba"), 103 | CYPRUS("Cyprus"), 104 | CZECH_REPUBLIC("Czech Republic"), 105 | DANZIG("Danzig"), 106 | DENMARK("Denmark"), 107 | DJIBOUTI("Djibouti"), 108 | DOMINICA("Dominica"), 109 | DOMINICAN_REPUBLIC("Dominican Republic"), 110 | EAST_TIMOR("East Timor"), 111 | ECUADOR("Ecuador"), 112 | EGYPT("Egypt"), 113 | EL_SALVADOR("El Salvador"), 114 | EQUATORIAL_GUINEA("Equatorial Guinea"), 115 | ERITREA("Eritrea"), 116 | ESTONIA("Estonia"), 117 | ETHIOPIA("Ethiopia"), 118 | FIJI("Fiji"), 119 | FINLAND("Finland"), 120 | FRANCE("France"), 121 | GABON("Gabon"), 122 | GAZA_STRIP("Gaza Strip"), 123 | THE_GAMBIA("The Gambia"), 124 | GEORGIA("Georgia"), 125 | GERMANY("Germany"), 126 | GHANA("Ghana"), 127 | GREECE("Greece"), 128 | GRENADA("Grenada"), 129 | GUATEMALA("Guatemala"), 130 | GUINEA("Guinea"), 131 | GUINEA_BISSAU("Guinea-Bissau"), 132 | GUYANA("Guyana"), 133 | HAITI("Haiti"), 134 | HOLY_ROMAN_EMPIRE("Holy Roman Empire"), 135 | HONDURAS("Honduras"), 136 | HUNGARY("Hungary"), 137 | ICELAND("Iceland"), 138 | INDIA("India"), 139 | INDONESIA("Indonesia"), 140 | IRAN("Iran"), 141 | IRAQ("Iraq"), 142 | REPUBLIC_OF_IRELAND("Republic of Ireland"), 143 | ISRAEL("Israel"), 144 | ITALY("Italy"), 145 | IVORY_COAST("Ivory Coast"), 146 | JAMAICA("Jamaica"), 147 | JAPAN("Japan"), 148 | JONATHANLAND("Jonathanland"), 149 | JORDAN("Jordan"), 150 | KAZAKHSTAN("Kazakhstan"), 151 | KENYA("Kenya"), 152 | KIRIBATI("Kiribati"), 153 | NORTH_KOREA("North Korea"), 154 | SOUTH_KOREA("South Korea"), 155 | KOSOVO("Kosovo"), 156 | KUWAIT("Kuwait"), 157 | KYRGYZSTAN("Kyrgyzstan"), 158 | LAOS("Laos"), 159 | LATVIA("Latvia"), 160 | LEBANON("Lebanon"), 161 | LESOTHO("Lesotho"), 162 | LIBERIA("Liberia"), 163 | LIBYA("Libya"), 164 | LIECHTENSTEIN("Liechtenstein"), 165 | LITHUANIA("Lithuania"), 166 | LUXEMBOURG("Luxembourg"), 167 | MACEDONIA("Macedonia"), 168 | MADAGASCAR("Madagascar"), 169 | MALAWI("Malawi"), 170 | MALAYSIA("Malaysia"), 171 | MALDIVES("Maldives"), 172 | MALI("Mali"), 173 | MALTA("Malta"), 174 | MARSHALL_ISLANDS("Marshall Islands"), 175 | MAURITANIA("Mauritania"), 176 | MAURITIUS("Mauritius"), 177 | MEXICO("Mexico"), 178 | MICRONESIA("Micronesia"), 179 | MOLDOVA("Moldova"), 180 | MONACO("Monaco"), 181 | MONGOLIA("Mongolia"), 182 | MONTENEGRO("Montenegro"), 183 | MOROCCO("Morocco"), 184 | MOUNT_ATHOS("Mount Athos"), 185 | MOZAMBIQUE("Mozambique"), 186 | NAMIBIA("Namibia"), 187 | NAURU("Nauru"), 188 | NEPAL("Nepal"), 189 | NEWFOUNDLAND("Newfoundland"), 190 | NETHERLANDS("Netherlands"), 191 | NEW_ZEALAND("New Zealand"), 192 | NICARAGUA("Nicaragua"), 193 | NIGER("Niger"), 194 | NIGERIA("Nigeria"), 195 | NORWAY("Norway"), 196 | OMAN("Oman"), 197 | OTTOMAN_EMPIRE("Ottoman Empire"), 198 | PAKISTAN("Pakistan"), 199 | PALAU("Palau"), 200 | PANAMA("Panama"), 201 | PAPUA_NEW_GUINEA("Papua New Guinea"), 202 | PARAGUAY("Paraguay"), 203 | PERU("Peru"), 204 | PHILIPPINES("Philippines"), 205 | POLAND("Poland"), 206 | PORTUGAL("Portugal"), 207 | PRUSSIA("Prussia"), 208 | QATAR("Qatar"), 209 | ROMANIA("Romania"), 210 | ROME("Rome"), 211 | RUSSIAN_FEDERATION("Russian Federation"), 212 | RWANDA("Rwanda"), 213 | GRENADINES("Grenadines"), 214 | SAMOA("Samoa"), 215 | SAN_MARINO("San Marino"), 216 | SAO_TOME_PRINCIPE("Sao Tome & Principe"), 217 | SAUDI_ARABIA("Saudi Arabia"), 218 | SENEGAL("Senegal"), 219 | SERBIA("Serbia"), 220 | SEYCHELLES("Seychelles"), 221 | SIERRA_LEONE("Sierra Leone"), 222 | SINGAPORE("Singapore"), 223 | SLOVAKIA("Slovakia"), 224 | SLOVENIA("Slovenia"), 225 | SOLOMON_ISLANDS("Solomon Islands"), 226 | SOMALIA("Somalia"), 227 | SOUTH_AFRICA("South Africa"), 228 | SPAIN("Spain"), 229 | SRI_LANKA("Sri Lanka"), 230 | SUDAN("Sudan"), 231 | SURINAME("Suriname"), 232 | SWAZILAND("Swaziland"), 233 | SWEDEN("Sweden"), 234 | SWITZERLAND("Switzerland"), 235 | SYRIA("Syria"), 236 | TAJIKISTAN("Tajikistan"), 237 | TANZANIA("Tanzania"), 238 | THAILAND("Thailand"), 239 | TOGO("Togo"), 240 | TONGA("Tonga"), 241 | TRINIDAD_TOBAGO("Trinidad & Tobago"), 242 | TUNISIA("Tunisia"), 243 | TURKEY("Turkey"), 244 | TURKMENISTAN("Turkmenistan"), 245 | TUVALU("Tuvalu"), 246 | UGANDA("Uganda"), 247 | UKRAINE("Ukraine"), 248 | UNITED_ARAB_EMIRATES("United Arab Emirates"), 249 | UNITED_KINGDOM("United Kingdom"), 250 | URUGUAY("Uruguay"), 251 | UZBEKISTAN("Uzbekistan"), 252 | VANUATU("Vanuatu"), 253 | VATICAN_CITY("Vatican City"), 254 | VENEZUELA("Venezuela"), 255 | VIETNAM("Vietnam"), 256 | YEMEN("Yemen"), 257 | ZAMBIA("Zambia"), 258 | ZIMBABWE("Zimbabwe"); 259 | */ 260 | private String country; 261 | 262 | Country(String country) { 263 | this.country = country; 264 | } 265 | 266 | public static Country getRandom() { 267 | return values()[new SecureRandom().nextInt(values().length)]; 268 | } 269 | 270 | @Override 271 | public String get() { 272 | return this.country; 273 | } 274 | 275 | } -------------------------------------------------------------------------------- /src/main/java/com/force/utility/DataLibrary.java: -------------------------------------------------------------------------------- 1 | package com.force.utility; 2 | 3 | import java.io.IOException; 4 | 5 | import org.apache.poi.xssf.usermodel.XSSFSheet; 6 | import org.apache.poi.xssf.usermodel.XSSFWorkbook; 7 | 8 | public class DataLibrary { 9 | public static Object[][] readExcelData(String excelfileName) { 10 | XSSFWorkbook wbook; 11 | Object[][] data = null ; 12 | try { 13 | wbook = new XSSFWorkbook("./data/"+excelfileName+".xlsx"); 14 | XSSFSheet sheet = wbook.getSheetAt(0); 15 | int rowCount = sheet.getLastRowNum(); 16 | int colCount = sheet.getRow(0).getLastCellNum(); 17 | data = new Object[rowCount][colCount]; 18 | for (int i = 1; i <= rowCount; i++) { 19 | for (int j = 0; j < colCount; j++) { 20 | data[i-1][j] = sheet.getRow(i).getCell(j).getStringCellValue(); 21 | } 22 | } 23 | wbook.close(); 24 | } catch (IOException e) { 25 | System.err.println(e.getMessage()); 26 | } 27 | return data; 28 | } 29 | } -------------------------------------------------------------------------------- /src/main/java/com/force/utility/HtmlReporter.java: -------------------------------------------------------------------------------- 1 | package com.force.utility; 2 | 3 | import java.io.File; 4 | import java.text.SimpleDateFormat; 5 | import java.util.Date; 6 | 7 | import com.aventstack.extentreports.ExtentReports; 8 | import com.aventstack.extentreports.ExtentTest; 9 | import com.aventstack.extentreports.MediaEntityModelProvider; 10 | import com.aventstack.extentreports.Status; 11 | import com.aventstack.extentreports.reporter.ExtentHtmlReporter; 12 | import com.aventstack.extentreports.reporter.configuration.ChartLocation; 13 | import com.aventstack.extentreports.reporter.configuration.Theme; 14 | import com.force.base.DriverFactory; 15 | import com.force.config.ConfigurationManager; 16 | 17 | public abstract class HtmlReporter extends DriverFactory { 18 | 19 | private static ExtentReports extent; 20 | private static final ThreadLocal parentTest = new ThreadLocal(); 21 | private static final ThreadLocal test = new ThreadLocal(); 22 | private static final ThreadLocal testName = new ThreadLocal(); 23 | 24 | 25 | private String fileName = "result.html"; 26 | private static String pattern = "dd-MMM-yyyy HH-mm-ss"; 27 | 28 | public String testcaseName, testDescription, authors, category, dataFileName, dataFileType; 29 | public static String folderName = ""; 30 | 31 | public static String createFolder(String folderName) { 32 | String date = new SimpleDateFormat(pattern).format(new Date()); 33 | folderName = folderName+"/" + date; 34 | 35 | File folder = new File("./" + folderName); 36 | if (!folder.exists()) { 37 | folder.mkdir(); 38 | } 39 | return folderName; 40 | } 41 | 42 | public synchronized void startReport() { 43 | folderName = createFolder("reports"); 44 | ExtentHtmlReporter htmlReporter = new ExtentHtmlReporter("./" + folderName + "/" + fileName); 45 | htmlReporter.config().setTestViewChartLocation(ChartLocation.BOTTOM); 46 | htmlReporter.config().setChartVisibilityOnOpen(!true); 47 | htmlReporter.config().setTheme(Theme.STANDARD); 48 | if(ConfigurationManager.configuration().reportTheme().equalsIgnoreCase("dark")) 49 | htmlReporter.config().setTheme(Theme.DARK); 50 | htmlReporter.config().setDocumentTitle(ConfigurationManager.configuration().reportTitle()); 51 | htmlReporter.config().setEncoding("utf-8"); 52 | htmlReporter.config().setReportName(ConfigurationManager.configuration().reportName()); 53 | htmlReporter.setAppendExisting(true); 54 | extent = new ExtentReports(); 55 | extent.attachReporter(htmlReporter); 56 | } 57 | 58 | public synchronized void startTestCase() { 59 | ExtentTest parent = extent.createTest(testcaseName, testDescription); 60 | parent.assignCategory(category); 61 | parent.assignAuthor(authors); 62 | parentTest.set(parent); 63 | testName.set(testcaseName); 64 | } 65 | 66 | public synchronized void setNode() { 67 | ExtentTest child = parentTest.get().createNode(getTestName()); 68 | test.set(child); 69 | } 70 | 71 | public abstract String takeSnap(); 72 | 73 | public void reportStep(String desc, String status, boolean bSnap) { 74 | synchronized (test) { 75 | 76 | // Start reporting the step and snapshot 77 | MediaEntityModelProvider img = null; 78 | if (bSnap && !(status.equalsIgnoreCase("INFO") || status.equalsIgnoreCase("skipped"))) { 79 | img = MediaEntityBuilder.createScreenCapture(takeSnap(),"Snap",true).build(); 80 | } 81 | if (status.equalsIgnoreCase("pass")) { 82 | test.get().pass(desc, img); 83 | } else if (status.equalsIgnoreCase("fail")) { // additional steps to manage alert pop-up 84 | test.get().fail(desc, img); 85 | throw new RuntimeException("See the reporter for details."); 86 | 87 | } else if (status.equalsIgnoreCase("warning")) { 88 | test.get().warning(desc, img); 89 | } else if (status.equalsIgnoreCase("skipped")) { 90 | test.get().skip("The test is skipped due to dependency failure"); 91 | } else if (status.equalsIgnoreCase("INFO")) { 92 | test.get().info(desc); 93 | } 94 | 95 | 96 | } 97 | } 98 | 99 | public void reportStep(String desc, String status) { 100 | reportStep(desc, status, true); 101 | } 102 | 103 | public synchronized void endResult() { 104 | extent.flush(); 105 | } 106 | 107 | 108 | public String getTestName() { 109 | return testName.get(); 110 | } 111 | 112 | public Status getTestStatus() { 113 | return parentTest.get().getModel().getStatus(); 114 | } 115 | 116 | 117 | } -------------------------------------------------------------------------------- /src/main/java/com/force/utility/MediaEntityBuilder.java: -------------------------------------------------------------------------------- 1 | package com.force.utility; 2 | 3 | import java.io.IOException; 4 | 5 | import com.aventstack.extentreports.MediaEntityModelProvider; 6 | import com.aventstack.extentreports.model.Media; 7 | import com.aventstack.extentreports.model.MediaType; 8 | import com.aventstack.extentreports.model.ScreenCapture; 9 | 10 | public class MediaEntityBuilder { 11 | 12 | private static ThreadLocal media; 13 | 14 | private static class MediaBuilderInstance { 15 | static final MediaEntityBuilder INSTANCE = new MediaEntityBuilder(); 16 | 17 | private MediaBuilderInstance() { } 18 | } 19 | 20 | private MediaEntityBuilder() { } 21 | 22 | private static MediaEntityBuilder getInstance() { 23 | return MediaBuilderInstance.INSTANCE; 24 | } 25 | 26 | public MediaEntityModelProvider build() { 27 | return new MediaEntityModelProvider(media.get()); 28 | } 29 | 30 | public static MediaEntityBuilder createScreenCaptureFromPath(String path, String title) throws IOException { 31 | if (path == null || path.isEmpty()) 32 | throw new IOException("ScreenCapture path cannot be null or empty."); 33 | 34 | return createScreenCapture(path, title, false); 35 | } 36 | 37 | public static MediaEntityBuilder createScreenCaptureFromPath(String path) throws IOException { 38 | return createScreenCaptureFromPath(path, null); 39 | } 40 | 41 | public static MediaEntityBuilder createScreenCaptureFromBase64String(String base64String) throws IOException { 42 | if (base64String == null || base64String.trim().equals("")) 43 | throw new IOException("Base64 string cannot be null or empty."); 44 | 45 | return createScreenCapture(base64String, null, true); 46 | } 47 | 48 | public static MediaEntityBuilder createScreenCapture(String pathOrBase64String, String title, boolean isBase64String) { 49 | ScreenCapture sc = new ScreenCapture(); 50 | sc.setMediaType(MediaType.IMG); 51 | if (isBase64String) 52 | sc.setBase64String(pathOrBase64String); 53 | else 54 | sc.setPath(pathOrBase64String); 55 | 56 | if (title != null) 57 | sc.setName(title); 58 | 59 | 60 | media = new ThreadLocal(); 61 | media.set(sc); 62 | 63 | return getInstance(); 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/com/force/utility/RetryTests.java: -------------------------------------------------------------------------------- 1 | package com.force.utility; 2 | 3 | import java.lang.reflect.Constructor; 4 | import java.lang.reflect.Method; 5 | 6 | import org.testng.IAnnotationTransformer; 7 | import org.testng.IRetryAnalyzer; 8 | import org.testng.ITestResult; 9 | import org.testng.annotations.ITestAnnotation; 10 | 11 | import com.force.base.ProjectHooks; 12 | import com.force.config.ConfigurationManager; 13 | 14 | public class RetryTests extends ProjectHooks implements IRetryAnalyzer, IAnnotationTransformer{ 15 | 16 | int retryCount = 0; 17 | 18 | @Override 19 | public boolean retry(ITestResult result) { 20 | if(retryCount < ConfigurationManager.configuration().maxRetry()) { 21 | retryCount++; 22 | return true; 23 | } 24 | return false; 25 | } 26 | 27 | @SuppressWarnings("rawtypes") 28 | @Override 29 | public void transform(ITestAnnotation annotation, Class testClass, Constructor testConstructor, Method testMethod) { 30 | annotation.setRetryAnalyzer(this.getClass()); 31 | } 32 | 33 | 34 | 35 | 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/com/force/utility/TempMailer.java: -------------------------------------------------------------------------------- 1 | package com.force.utility; 2 | 3 | import java.nio.file.Paths; 4 | 5 | import com.force.base.ProjectHooks; 6 | import com.force.config.ConfigurationManager; 7 | import com.microsoft.playwright.Browser; 8 | import com.microsoft.playwright.BrowserContext; 9 | import com.microsoft.playwright.BrowserType; 10 | import com.microsoft.playwright.Locator; 11 | import com.microsoft.playwright.Page; 12 | import com.microsoft.playwright.Playwright; 13 | 14 | public class TempMailer extends ProjectHooks{ 15 | 16 | public static String getEmailAddress() { 17 | String innerText = ""; 18 | try { 19 | Playwright pw = Playwright.create(); 20 | Browser browser = pw.chromium().launch(new BrowserType.LaunchOptions().setChannel("chrome").setHeadless(true)); 21 | BrowserContext newContext = browser.newContext(); 22 | Page page = newContext.newPage(); 23 | page.navigate("https://www.sharklasers.com/", new Page.NavigateOptions().setTimeout(0)); 24 | innerText = page.locator("#inbox-id").innerText()+"@sharklasers.com"; 25 | System.out.println(innerText); 26 | if(!innerText.trim().equals("")) { 27 | email.set(innerText); 28 | newEmails.put(innerText, "Created"); 29 | } 30 | newContext.storageState(new BrowserContext.StorageStateOptions().setPath(Paths.get("storage/"+innerText+".json"))); 31 | pw.close(); 32 | }catch(Exception e) {} 33 | return innerText; 34 | } 35 | 36 | public static String getRegistrationLink(String email) { 37 | boolean bEmailReceived = false; 38 | int maxEmailWaitTime = 0; 39 | String attribute = ""; 40 | try { 41 | Playwright pw = Playwright.create(); 42 | Browser browser = pw.chromium().launch(new BrowserType.LaunchOptions().setChannel("chrome").setHeadless(true)); 43 | BrowserContext context = browser.newContext(new Browser.NewContextOptions().setStorageStatePath(Paths.get("storage/"+email+".json"))); 44 | Page page = context.newPage(); 45 | 46 | while(!bEmailReceived && maxEmailWaitTime < ConfigurationManager.configuration().maxEmailTimeout()) { 47 | page.navigate("https://www.sharklasers.com/", new Page.NavigateOptions().setTimeout(0)); 48 | Locator locator = page.locator("(//tr[contains(@class,'mail_row email_unread')]/td[@class='td2'])[1]"); 49 | if(locator.count() == 1) { 50 | locator.click(); 51 | try { 52 | page.setDefaultTimeout(2000); 53 | attribute = page.locator("//a[contains(text(),'Verify Account')]").getAttribute("href"); 54 | newEmails.put(email, "EmailConfirmed"); 55 | bEmailReceived = true; 56 | break; 57 | }catch(Exception e) { 58 | //e.printStackTrace(); 59 | } 60 | } else { 61 | System.out.println("No emails yet !!"); 62 | } 63 | page.setDefaultTimeout(ConfigurationManager.configuration().timeout()); 64 | 65 | if(!bEmailReceived) { 66 | page.goBack(); 67 | try {Thread.sleep(10000);} catch(Exception e) {} 68 | maxEmailWaitTime = maxEmailWaitTime + 12000; 69 | } 70 | } 71 | pw.close(); 72 | } catch(Exception e) { 73 | e.printStackTrace(); 74 | } 75 | return attribute; 76 | } 77 | 78 | } 79 | -------------------------------------------------------------------------------- /src/main/resources/app.properties: -------------------------------------------------------------------------------- 1 | # target execution: local or remote 2 | target = local 3 | 4 | # The base API Endpoint 5 | url.api = https://qeagle-dev-ed.my.salesforce.com/services/data/v56.0/sobjects/ 6 | 7 | # The base URL to open the application in browser 8 | url.base = https://qeagle-dev-ed.my.salesforce.com/ 9 | 10 | # The base URL to authenticate OAuth 11 | url.oauth = https://login.salesforce.com/services/oauth2/token 12 | -------------------------------------------------------------------------------- /src/main/resources/grid.properties: -------------------------------------------------------------------------------- 1 | # grid url and port 2 | grid.url = localhost 3 | grid.port = 4444 4 | -------------------------------------------------------------------------------- /src/main/resources/local.properties: -------------------------------------------------------------------------------- 1 | # The set of local properties to execute the tests ! 2 | 3 | # The default browser to run - chrome / msedge / firefox 4 | browser = chrome 5 | 6 | # Faker Language 7 | faker.locale = pt-EN 8 | 9 | # The mode at which the browser runs 10 | headless = false 11 | 12 | # The default timeout 13 | timeout = 30000 14 | 15 | # Auto Login 16 | auto.login = true 17 | 18 | # Enable Tracing 19 | enable.tracing = true 20 | 21 | # Execution Speed Control 22 | action.delay = 200 23 | 24 | # The different pause timers 25 | pause.low = 1000 26 | pause.medium = 2000 27 | pause.high = 5000 28 | 29 | # Max wait time for email check 30 | email.max.timeout = 300000 31 | 32 | # Max Retry on failure 33 | max.retry = 2 34 | 35 | # Credentials for the app environment 36 | app.username = hari.radhakrishnan@qeagle.com 37 | app.password = Testleaf$321 38 | 39 | # Client Id and Client Secret 40 | client.id = 3MVG9fe4g9fhX0E78G.JkSsPzjwcS9kBXcm7kjvUsSIRHhQMG80LJ.PXbfzFyc2Eu6q3ZRfomtJL47syintYu 41 | client.secret = F8624FE2A89A3EBDCD8B517222905ACE5FC4F502FA1AB567375B31C2B2F3289F 42 | 43 | 44 | -------------------------------------------------------------------------------- /src/main/resources/report.properties: -------------------------------------------------------------------------------- 1 | 2 | # Extent report configuration 3 | report.title = TestLeaf Automation Testing 4 | report.name = Playwright Automation 5 | report.theme = dark -------------------------------------------------------------------------------- /src/test/java/page/base/HomePage.java: -------------------------------------------------------------------------------- 1 | package page.base; 2 | 3 | import com.force.base.ProjectHooks; 4 | 5 | public class HomePage extends ProjectHooks{ 6 | 7 | public HomePage clickAppLauncher() { 8 | click(".slds-icon-waffle", "App Launcher", "Icon"); 9 | return this; 10 | } 11 | 12 | public HomePage clickViewAll() { 13 | click("(//button[text()='View All'])[1]", "View All"); 14 | return this; 15 | } 16 | 17 | public HomePage typeSearchApps(String appName) { 18 | type("(//input[@placeholder='Search apps or items...'])[1]", appName, "Search Apps"); 19 | return this; 20 | } 21 | 22 | public HomePage clickApp(String appName) { 23 | click("(//span[text()='All Apps']/following::mark[text()='"+appName+"'])[1]", appName); 24 | return this; 25 | } 26 | 27 | public HomePage clickLeftMenu(String menuName) { 28 | click("//a[text()='"+menuName+"']", menuName, "Menu"); 29 | return this; 30 | } 31 | 32 | public HomePage clickLeftSubMenu(String mainMenuName, String subMenu) { 33 | click("//a[@class='slds-tree__item-label' and text()='"+mainMenuName+"']/following::a[text()='"+subMenu+"']", subMenu, "Sub Menu"); 34 | return this; 35 | } 36 | 37 | } 38 | 39 | -------------------------------------------------------------------------------- /src/test/java/page/base/LoginPage.java: -------------------------------------------------------------------------------- 1 | package page.base; 2 | 3 | import java.nio.file.Paths; 4 | 5 | import com.force.base.ProjectHooks; 6 | import com.force.config.ConfigurationManager; 7 | import com.microsoft.playwright.BrowserContext; 8 | 9 | public class LoginPage extends ProjectHooks{ 10 | 11 | public LoginPage typeUserName(String username) { 12 | type("#username", username, "Username"); 13 | return this; 14 | } 15 | 16 | public LoginPage typePassword(String password) { 17 | type("#password", password, "Password"); 18 | return this; 19 | } 20 | 21 | public LoginPage clickLogin() { 22 | click("#Login", "Log In"); 23 | return this; 24 | } 25 | 26 | public LoginPage doLogin() { 27 | if(getPage().title().contains("Login")) { 28 | typeUserName(ConfigurationManager.configuration().appUserName()) 29 | .typePassword(ConfigurationManager.configuration().appPassword()) 30 | .clickLogin(); 31 | 32 | getContext().storageState(new BrowserContext.StorageStateOptions().setPath(Paths.get("storage/login.json"))); 33 | } 34 | return this; 35 | 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/test/java/page/base/SalesforceHooks.java: -------------------------------------------------------------------------------- 1 | package page.base; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | import org.testng.annotations.BeforeMethod; 7 | 8 | import com.force.base.ProjectHooks; 9 | import com.force.data.dynamic.FakerDataFactory; 10 | 11 | public class SalesforceHooks extends ProjectHooks{ 12 | 13 | @BeforeMethod 14 | public void zLogin() { 15 | new LoginPage().doLogin(); 16 | } 17 | 18 | public void typeInputField(String label, String value) { 19 | type("//label[text()='"+label+"']/following::input[1]", value, label); 20 | } 21 | 22 | public void typeInputInFrame(String label, String value) { 23 | typeInFrame("//label[text()='"+label+"']/following::input[1]", value, label); 24 | } 25 | 26 | public void chooseByText(String label, String value) { 27 | clickAndChoose("//label[text()='"+label+"']/following::lightning-base-combobox[1]", "(//label[text()='"+label+"']/following::lightning-base-combobox[1]//lightning-base-combobox-item//span[text()='"+value+"'])[1]", value, label); 28 | } 29 | 30 | public void chooseByTextInFrame(String label, String value) { 31 | clickAndChooseInFrame("//label[text()='"+label+"']/following::lightning-base-combobox[1]", "(//label[text()='"+label+"']/following::lightning-base-combobox[1]//lightning-base-combobox-item//span[text()='"+value+"'])[1]", value, label); 32 | } 33 | 34 | public void verifyMandatory(String label) { 35 | verifyAttribute("(//label[text()='"+label+"']/following::td[1]//div[1])[1]", "class","requiredInput"); 36 | } 37 | 38 | public void verifyMandatoryInFrame(String label) { 39 | verifyAttributeInFrame("(//label[text()='"+label+"']/following::td[1]//div[1])[1]", "class","requiredInput"); 40 | } 41 | 42 | public String createLeadUsingApi() { 43 | 44 | Map headers = new HashMap<>(); 45 | headers.put("Authorization", "Bearer "+getToken()); 46 | headers.put("Content-Type", "application/json"); 47 | 48 | String firstName = FakerDataFactory.getFirstName(); 49 | String lastName = FakerDataFactory.getLastName(); 50 | String companyName = FakerDataFactory.getCompanyName(); 51 | 52 | String jsonBody = "{\n" 53 | + " \"FirstName\": \""+firstName+"\",\n" 54 | + " \"LastName\": \""+lastName+"\",\n" 55 | + " \"Company\": \""+companyName+"\",\n" 56 | + " \"LeadSource\" : \"Other\"\n" 57 | + "}"; 58 | 59 | postRequest("Lead/", headers, jsonBody); 60 | 61 | return firstName; 62 | } 63 | 64 | public String createAccountUsingApi() { 65 | 66 | Map headers = new HashMap<>(); 67 | headers.put("Authorization", "Bearer "+getToken()); 68 | headers.put("Content-Type", "application/json"); 69 | 70 | String companyName = FakerDataFactory.getCompanyName(); 71 | String accountNumber = FakerDataFactory.getBankAccountNumber(); 72 | String rating = FakerDataFactory.getRating(); 73 | 74 | String jsonBody = "{\n" 75 | + " \"Name\": \""+companyName+"\",\n" 76 | + " \"AccountNumber\": \""+accountNumber+"\",\n" 77 | + " \"Rating\": \""+rating+"\"\n" 78 | + "}"; 79 | 80 | postRequest("Account/", headers, jsonBody); 81 | 82 | return companyName; 83 | } 84 | } 85 | 86 | 87 | -------------------------------------------------------------------------------- /src/test/java/page/services/AccountPage.java: -------------------------------------------------------------------------------- 1 | package page.services; 2 | 3 | import com.force.data.dynamic.FakerDataFactory; 4 | 5 | import page.base.SalesforceHooks; 6 | 7 | public class AccountPage extends SalesforceHooks{ 8 | 9 | public AccountPage typeAccountName() { 10 | typeInputField("Account Name", FakerDataFactory.getFirstName()); 11 | return this; 12 | } 13 | 14 | public AccountPage typeAccountNumber() { 15 | typeInputField("Account Number", FakerDataFactory.getBankAccountNumber()); 16 | return this; 17 | } 18 | 19 | public AccountPage chooseRating() { 20 | chooseByText("Rating", FakerDataFactory.getRating()); 21 | return this; 22 | } 23 | 24 | public AccountPage clickSave() { 25 | click("(//button[text()='Save'])[1]", "Save"); 26 | return this; 27 | } 28 | 29 | public AccountPage verifyAccountCreated() { 30 | // Print the bearer token 31 | String token = getToken(); 32 | System.out.println(token); 33 | 34 | return this; 35 | } 36 | 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/test/java/page/services/LeadPage.java: -------------------------------------------------------------------------------- 1 | package page.services; 2 | 3 | import com.force.data.dynamic.FakerDataFactory; 4 | 5 | import page.base.SalesforceHooks; 6 | 7 | public class LeadPage extends SalesforceHooks{ 8 | 9 | public String fillMandatoryFields() { 10 | String firstName = FakerDataFactory.getFirstName(); 11 | String lastName = FakerDataFactory.getLastName(); 12 | String companyName = FakerDataFactory.getCompanyName(); 13 | String salutation = FakerDataFactory.getSalutation(); 14 | 15 | chooseSalutation(salutation) 16 | .typeFirstName(firstName) 17 | .typeLastName(lastName) 18 | .typeCompanyName(companyName) 19 | .chooseLeadStatus() 20 | .clickSave(); 21 | 22 | return salutation+" "+firstName+" "+lastName; 23 | } 24 | 25 | public LeadPage typeFirstName(String firstName) { 26 | typeInputField("First Name", firstName); 27 | return this; 28 | } 29 | 30 | public LeadPage typeLastName(String lastName) { 31 | typeInputField("Last Name", lastName); 32 | return this; 33 | } 34 | 35 | public LeadPage typeCompanyName(String companyName) { 36 | typeInputField("Company", companyName); 37 | return this; 38 | } 39 | 40 | public LeadPage chooseSalutation(String salutation) { 41 | chooseByText("Salutation", salutation); 42 | return this; 43 | } 44 | 45 | public LeadPage chooseLeadStatus() { 46 | chooseByText("Lead Status", "Working - Contacted"); 47 | return this; 48 | } 49 | 50 | public LeadPage clickSave() { 51 | click("(//button[text()='Save'])[1]", "Save"); 52 | return this; 53 | } 54 | 55 | 56 | } 57 | -------------------------------------------------------------------------------- /src/test/java/page/services/ServiceDashboardPage.java: -------------------------------------------------------------------------------- 1 | package page.services; 2 | 3 | import com.force.data.dynamic.FakerDataFactory; 4 | 5 | import page.base.SalesforceHooks; 6 | 7 | public class ServiceDashboardPage extends SalesforceHooks{ 8 | 9 | public ServiceDashboardPage clickTab(String tabName) { 10 | click("//span[text()='"+tabName+"']", tabName, "Tab"); 11 | return this; 12 | } 13 | 14 | public ServiceDashboardPage clickMenu(String menuName) { 15 | click("//div[@title='"+menuName+"']", menuName, "Menu"); 16 | return this; 17 | } 18 | 19 | public ServiceDashboardPage typeSearch(String searchName) { 20 | typeAndEnter("//input[@placeholder='Search this list...']", searchName, "Search"); 21 | pause("medium"); 22 | return this; 23 | } 24 | 25 | public ServiceDashboardPage clickAction() { 26 | click("//table[@role='grid']//tr[1]/td[last()]", "Action"); 27 | return this; 28 | } 29 | 30 | public ServiceDashboardPage clickEdit() { 31 | click("(//a[@title='Edit'])[1]", "Edit"); 32 | return this; 33 | } 34 | 35 | public ServiceDashboardPage typePhone() { 36 | typeInputField("Phone", FakerDataFactory.getContactNumber()); 37 | return this; 38 | } 39 | 40 | public ServiceDashboardPage clickSave() { 41 | click("(//button[text()='Save'])[1]", "Save"); 42 | return this; 43 | } 44 | 45 | public ServiceDashboardPage verifyToastMessage(String name) { 46 | verifyPartialText("(//div[contains(@class,'toast forceToastMessage')]//a/div)[1]", name); 47 | return this; 48 | } 49 | 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/test/java/page/services/UsersPage.java: -------------------------------------------------------------------------------- 1 | package page.services; 2 | 3 | import java.util.List; 4 | 5 | import com.force.data.dynamic.FakerDataFactory; 6 | import com.force.utility.TempMailer; 7 | 8 | import page.base.SalesforceHooks; 9 | 10 | public class UsersPage extends SalesforceHooks{ 11 | 12 | public UsersPage typeFirstName() { 13 | typeInputInFrame("First Name", FakerDataFactory.getFirstName()); 14 | return this; 15 | } 16 | 17 | public UsersPage typeLastName() { 18 | typeInputInFrame("Last Name", FakerDataFactory.getLastName()); 19 | return this; 20 | } 21 | 22 | public UsersPage typeEmail() { 23 | typeInputInFrame("Email", TempMailer.getEmailAddress()); 24 | return this; 25 | } 26 | 27 | public UsersPage chooseProfile() { 28 | selectByIndexInFrame("#Profile", -1, "Profile"); 29 | return this; 30 | } 31 | 32 | public UsersPage clickNewUser() { 33 | frameLocator.set(getPage().frameLocator("(//iframe)[1]")); 34 | clickInFrame("(//input[@title='New User'])[1]", "New User"); 35 | return this; 36 | } 37 | 38 | public UsersPage verifyMandatoryFields(List labels) { 39 | for (int i = 0; i < labels.size(); i++) { 40 | verifyMandatoryInFrame(labels.get(i)); 41 | } 42 | return this; 43 | } 44 | 45 | public UsersPage clickSave() { 46 | clickInFrame("(//input[@name='save'])[1]", "Save"); 47 | return this; 48 | } 49 | 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/test/java/tests/ui/services/TC01_CreateAccount.java: -------------------------------------------------------------------------------- 1 | package tests.ui.services; 2 | 3 | import org.testng.annotations.Test; 4 | import org.testng.annotations.BeforeTest; 5 | 6 | import page.base.HomePage; 7 | import page.base.SalesforceHooks; 8 | import page.services.AccountPage; 9 | import page.services.ServiceDashboardPage; 10 | 11 | public class TC01_CreateAccount extends SalesforceHooks{ 12 | 13 | @BeforeTest 14 | public void setReportValues() { 15 | testcaseName = "TC01 - Create Account"; 16 | testDescription = "Create New Account with mandatory fields"; 17 | authors = "TestLeaf"; 18 | category = "Service"; 19 | } 20 | 21 | @Test 22 | public void createAccount() { 23 | 24 | new HomePage() 25 | .clickAppLauncher() 26 | .clickViewAll() 27 | .typeSearchApps("Service") 28 | .clickApp("Service"); 29 | 30 | new ServiceDashboardPage() 31 | .clickTab("Accounts") 32 | .clickMenu("New"); 33 | 34 | new AccountPage() 35 | .typeAccountName() 36 | .chooseRating() 37 | .typeAccountNumber() 38 | .clickSave(); 39 | 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/test/java/tests/ui/services/TC02_EditAccount.java: -------------------------------------------------------------------------------- 1 | package tests.ui.services; 2 | 3 | import org.testng.annotations.Test; 4 | import org.testng.annotations.BeforeTest; 5 | 6 | import page.base.HomePage; 7 | import page.base.SalesforceHooks; 8 | import page.services.AccountPage; 9 | import page.services.ServiceDashboardPage; 10 | 11 | public class TC02_EditAccount extends SalesforceHooks{ 12 | 13 | @BeforeTest 14 | public void setReportValues() { 15 | testcaseName = "TC02 - Edit Account"; 16 | testDescription = "Edit existing Account with rating change"; 17 | authors = "TestLeaf"; 18 | category = "Service"; 19 | } 20 | 21 | @Test 22 | public void editAccount() { 23 | 24 | String accountName = createAccountUsingApi(); 25 | 26 | new HomePage() 27 | .clickAppLauncher() 28 | .clickViewAll() 29 | .typeSearchApps("Service") 30 | .clickApp("Service"); 31 | 32 | new ServiceDashboardPage() 33 | .clickTab("Accounts") 34 | .typeSearch(accountName) 35 | .clickAction() 36 | .clickEdit(); 37 | 38 | new AccountPage() 39 | .chooseRating() 40 | .clickSave(); 41 | 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/test/java/tests/ui/services/TC03_CreateLead.java: -------------------------------------------------------------------------------- 1 | package tests.ui.services; 2 | 3 | import org.testng.annotations.Test; 4 | import org.testng.annotations.BeforeTest; 5 | 6 | import page.base.HomePage; 7 | import page.base.SalesforceHooks; 8 | import page.services.LeadPage; 9 | import page.services.ServiceDashboardPage; 10 | 11 | public class TC03_CreateLead extends SalesforceHooks{ 12 | 13 | @BeforeTest 14 | public void setReportValues() { 15 | testcaseName = "TC03 - Create Lead"; 16 | testDescription = "Create a new Lead"; 17 | authors = "TestLeaf"; 18 | category = "Service"; 19 | } 20 | 21 | @Test 22 | public void editAccount() { 23 | 24 | // Go to Marketing Application 25 | new HomePage() 26 | .clickAppLauncher() 27 | .clickViewAll() 28 | .typeSearchApps("Marketing") 29 | .clickApp("Marketing"); 30 | 31 | ServiceDashboardPage sdp = new ServiceDashboardPage(); 32 | 33 | // Create a new Leads 34 | sdp.clickTab("Leads").clickMenu("New"); 35 | 36 | // Fill all mandatory fields 37 | String toastMessage = new LeadPage().fillMandatoryFields(); 38 | 39 | // Verify that the lead is created 40 | sdp.verifyToastMessage(toastMessage); 41 | 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/test/java/tests/ui/services/TC04_EditLead.java: -------------------------------------------------------------------------------- 1 | package tests.ui.services; 2 | 3 | import org.testng.annotations.Test; 4 | import org.testng.annotations.BeforeTest; 5 | 6 | import page.base.HomePage; 7 | import page.base.SalesforceHooks; 8 | import page.services.ServiceDashboardPage; 9 | 10 | public class TC04_EditLead extends SalesforceHooks{ 11 | 12 | @BeforeTest 13 | public void setReportValues() { 14 | testcaseName = "TC04 - Edit Lead"; 15 | testDescription = "Edit an existing Lead"; 16 | authors = "TestLeaf"; 17 | category = "Service"; 18 | } 19 | 20 | @Test 21 | public void editAccount() { 22 | 23 | String firstName = createLeadUsingApi(); 24 | 25 | new HomePage() 26 | .clickAppLauncher() 27 | .clickViewAll() 28 | .typeSearchApps("Marketing") 29 | .clickApp("Marketing"); 30 | 31 | ServiceDashboardPage sdp = new ServiceDashboardPage(); 32 | 33 | sdp.clickTab("Leads") 34 | .typeSearch(firstName) 35 | .clickAction() 36 | .clickEdit() 37 | .typePhone() 38 | .clickSave(); 39 | 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /storage/login.json: -------------------------------------------------------------------------------- 1 | {"cookies":[{"name":"CookieConsentPolicy","value":"0:1","domain":"qeagle-dev-ed.my.salesforce.com","path":"/","expires":1700454754.975064,"httpOnly":false,"secure":false,"sameSite":"Lax"},{"name":"LSKey-c$CookieConsentPolicy","value":"0:1","domain":"qeagle-dev-ed.my.salesforce.com","path":"/","expires":1700454754.97521,"httpOnly":false,"secure":false,"sameSite":"Lax"},{"name":"BrowserId","value":"WwQerGiMEe2ue8khyLQOaw","domain":".salesforce.com","path":"/","expires":1700454754.975236,"httpOnly":false,"secure":false,"sameSite":"Lax"},{"name":"BrowserId_sec","value":"WwQerGiMEe2ue8khyLQOaw","domain":".salesforce.com","path":"/","expires":1700454754.975253,"httpOnly":false,"secure":true,"sameSite":"None"},{"name":"CookieConsentPolicy","value":"0:0","domain":"login.salesforce.com","path":"/","expires":1700454755.952124,"httpOnly":false,"secure":false,"sameSite":"Lax"},{"name":"LSKey-c$CookieConsentPolicy","value":"0:0","domain":"login.salesforce.com","path":"/","expires":1700454755.952164,"httpOnly":false,"secure":false,"sameSite":"Lax"},{"name":"session","value":"1668918756118","domain":"login.salesforce.com","path":"/","expires":-1,"httpOnly":false,"secure":true,"sameSite":"None"},{"name":"v0","value":"SFDC%20Network%7C%5BSalesforce.com%20App%5D","domain":".c.salesforce.com","path":"/","expires":-1,"httpOnly":false,"secure":true,"sameSite":"Lax"},{"name":"lastvaliddriver","value":"SFDC%20Network%7C%5BSalesforce.com%20App%5D","domain":".c.salesforce.com","path":"/","expires":1669523556,"httpOnly":false,"secure":true,"sameSite":"Lax"},{"name":"c22","value":"SFDC%20Network","domain":".c.salesforce.com","path":"/","expires":-1,"httpOnly":false,"secure":true,"sameSite":"Lax"},{"name":"cvtdt","value":"SFDC%20Network","domain":".c.salesforce.com","path":"/","expires":1703478757.27836,"httpOnly":false,"secure":true,"sameSite":"Lax"},{"name":"c40","value":"SFDC%20Network","domain":".c.salesforce.com","path":"/","expires":-1,"httpOnly":false,"secure":true,"sameSite":"Lax"},{"name":"v20","value":"Direct%20Landing","domain":".c.salesforce.com","path":"/","expires":-1,"httpOnly":false,"secure":true,"sameSite":"Lax"},{"name":"convertingPageUrl","value":"Direct%20Landing","domain":".c.salesforce.com","path":"/","expires":-1,"httpOnly":false,"secure":true,"sameSite":"Lax"},{"name":"cleared-onetrust-cookies","value":"","domain":".salesforce.com","path":"/","expires":1700668786,"httpOnly":false,"secure":false,"sameSite":"Lax"},{"name":"inst","value":"APP_5g","domain":".salesforce.com","path":"/","expires":-1,"httpOnly":false,"secure":true,"sameSite":"None"},{"name":"rememberUn","value":"false","domain":".salesforce.com","path":"/","expires":1674296989.900024,"httpOnly":false,"secure":true,"sameSite":"None"},{"name":"com.salesforce.LocaleInfo","value":"us","domain":".salesforce.com","path":"/","expires":1674296989.900082,"httpOnly":false,"secure":true,"sameSite":"None"},{"name":"oinfo","value":"c3RhdHVzPUZSRUUmdHlwZT0zJm9pZD0wMEQ1ZzAwMDAwN29SclU=","domain":".salesforce.com","path":"/","expires":1674296989.900115,"httpOnly":false,"secure":true,"sameSite":"None"},{"name":"disco","value":"5g:00D5g000007oRrU:0055g00000B238K:1","domain":".salesforce.com","path":"/","expires":-1,"httpOnly":false,"secure":true,"sameSite":"None"},{"name":"autocomplete","value":"1","domain":".salesforce.com","path":"/","expires":1674296990.109898,"httpOnly":false,"secure":true,"sameSite":"None"},{"name":"sid_Client","value":"g00000B238Kg000007oRrU","domain":"qeagle-dev-ed.my.salesforce.com","path":"/","expires":-1,"httpOnly":false,"secure":true,"sameSite":"None"},{"name":"oid","value":"00D5g000007oRrU","domain":"qeagle-dev-ed.my.salesforce.com","path":"/","expires":1700648990.110045,"httpOnly":false,"secure":true,"sameSite":"None"},{"name":"BrowserId","value":"ZsFWImiMEe2veGE2VHVjXw","domain":".force.com","path":"/","expires":1700454774.683432,"httpOnly":false,"secure":false,"sameSite":"Lax"},{"name":"BrowserId_sec","value":"ZsFWImiMEe2veGE2VHVjXw","domain":".force.com","path":"/","expires":1700454774.683447,"httpOnly":false,"secure":true,"sameSite":"None"},{"name":"sid_Client","value":"g00000B238Kg000007oRrU","domain":"qeagle-dev-ed.file.force.com","path":"/","expires":-1,"httpOnly":false,"secure":true,"sameSite":"None"},{"name":"inst","value":"APP_5g","domain":".force.com","path":"/","expires":-1,"httpOnly":false,"secure":true,"sameSite":"None"},{"name":"CookieConsentPolicy","value":"0:1","domain":"qeagle-dev-ed.file.force.com","path":"/","expires":1700454774.884809,"httpOnly":false,"secure":false,"sameSite":"Lax"},{"name":"LSKey-c$CookieConsentPolicy","value":"0:1","domain":"qeagle-dev-ed.file.force.com","path":"/","expires":1700454774.884912,"httpOnly":false,"secure":false,"sameSite":"Lax"},{"name":"_ga_S6WMKB0ZK3","value":"GS1.1.1669007933.2.0.1669007933.0.0.0","domain":".salesforce.com","path":"/","expires":1703567933.100593,"httpOnly":false,"secure":false,"sameSite":"Lax"},{"name":"_ga","value":"GA1.1.1779302067.1668918759","domain":".salesforce.com","path":"/","expires":1703567933.103826,"httpOnly":false,"secure":false,"sameSite":"Lax"},{"name":"79eb100099b9a8bf","value":"3:false:.salesforce.com","domain":".salesforce.com","path":"/","expires":-1,"httpOnly":false,"secure":true,"sameSite":"None"},{"name":"webact","value":"%7B%22l_vdays%22%3A2%2C%22l_visit%22%3A1669007930278%2C%22session%22%3A1669112982309%2C%22l_search%22%3A%22%22%2C%22l_dtype%22%3A%22SFDC%20Network%22%2C%22l_page%22%3A%22SFDC%3Aus%3Alogin%3Adeveloper%22%2C%22l_page_url%22%3A%22https%3A%2F%2Fc.salesforce.com%2Flogin-messages%2Fpromos.html%22%2C%22counter%22%3A2%2C%22pv%22%3A1%2C%22f_visit%22%3A1668918756413%2C%22seg%22%3A%22non-customer%3Aus%22%2C%22d%22%3A%2270130000000sUW0%22%2C%22developer%22%3A1669007930278%7D","domain":".salesforce.com","path":"/","expires":1700648985,"httpOnly":false,"secure":true,"sameSite":"Lax"},{"name":"sfdc_lv2","value":"KxG83Hz9FA4XM8NUcPvpomgDRqVxmrUJ8QsA2r8+7h7lMv/X7VsEmuPKFDt7tnYbs=","domain":".salesforce.com","path":"/","expires":1703672990.109828,"httpOnly":false,"secure":true,"sameSite":"None"},{"name":"sid","value":"00D5g000007oRrU!AQkAQCO0Mh6nKuR9d1f9tNUlAhoPkXWuuAB883CpXfGiuSFB.MIdIePG14DL5babKfmphswn30HsLPqtV5yg2sbyYjq02xhv","domain":"qeagle-dev-ed.my.salesforce.com","path":"/","expires":-1,"httpOnly":false,"secure":true,"sameSite":"None"},{"name":"clientSrc","value":"183.82.24.56","domain":"qeagle-dev-ed.my.salesforce.com","path":"/","expires":-1,"httpOnly":false,"secure":true,"sameSite":"None"},{"name":"sid","value":"00D5g000007oRrU!AQkAQM2E1PoucdWkdYABed2vXTtkv_8nQLVIpIIuh6Kpp6tTntexKL2GmFON0zCG4a0gUfg48ppDj3kqJmUxTgmCwpBz5a.S","domain":"qeagle-dev-ed.file.force.com","path":"/","expires":-1,"httpOnly":false,"secure":true,"sameSite":"None"},{"name":"clientSrc","value":"183.82.24.56","domain":"qeagle-dev-ed.file.force.com","path":"/","expires":-1,"httpOnly":false,"secure":true,"sameSite":"None"}],"origins":[{"origin":"https://c.salesforce.com","localStorage":[{"name":"webactls","value":"{\"chat\":0,\"kxsfdc\":{\"segs\":\"\"},\"l_cloud\":\"No Cloud\",\"lastvaliddriver\":\"SFDC Network|[Salesforce.com App]\"}"}]}]} -------------------------------------------------------------------------------- /testng.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | --------------------------------------------------------------------------------