├── 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 | Dependency
11 | Version
12 | Feature
13 |
14 |
15 |
16 |
17 | Playwright
18 | 1.28.0
19 | End to End Automation - Browser & API
20 |
21 |
22 | TestNG
23 | 7.4.0
24 | The Test Runner to execute suite
25 |
26 |
27 | JSON
28 | 20220924
29 | Create & Parse JSON Files for API
30 |
31 |
32 | Apache POI
33 | 5.2.3
34 | Read Excel files for test data
35 |
36 |
37 | Data Faker
38 | 1.6.0
39 | Create runtime fake test data
40 |
41 |
42 | Owner
43 | 1.0.12
44 | Minimize the properties file code
45 |
46 |
47 | Extent Reports
48 | 3.1.5
49 | The HTML reporting library
50 |
51 |
52 |
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 |
76 |
77 |
78 | Single framework designed using bridge pattern to allow conversation between UI and API simultaneously.
79 |
80 |
81 | You can create data using API and use that data to your UI tests to make your tests independent.
82 |
83 |
84 | Your UI tests can execute the test and as part of the assertions, it make sense to validate using API.
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 | ✅ Fastest test execution 🚀 with configurable speed control
93 |
94 |
95 | Playwright engine is comparatively (above 30% on average) faster than other UI automation frameworks.
96 |
97 | Sometimes it requires a slow down to eliminate the script flakiness with configurable speed included through the listeners.
98 |
99 |
100 |
101 | ```java
102 | // Sample code to control your delays
103 | setSlowMo(ConfigurationManager.configuration().slowMotion());
104 | ```
105 |
106 |
107 |
108 |
109 | ✅ Debug Faster with Snaps, Videos 🎥 and Tracings with configurable options
110 |
111 |
112 |
113 | Playwright library provides full/partial snaps, videos (webm) and trace viewer that captures network calls.
114 |
115 |
116 | Our framework allows configuration for framework user to either plugin on demand for every run or failures.
117 |
118 |
119 |
120 | ```java
121 | // Sample code to control your delays
122 | setRecordVideoDir(Paths.get(folderName));
123 | ```
124 |
125 |
126 |
127 | ✅ Automated logins 💡 to avoid too many login tests
128 |
129 |
130 |
131 | Configurable automated logins can avoid unnecessary login tests through storing the state of the user.
132 |
133 |
134 | The user can either use the existing login storage or decide to login automated through configuration.
135 |
136 |
137 |
138 | ```java
139 | # Auto Login
140 | auto.login = true
141 | ```
142 |
143 |
144 | ✅ Automated retries 🔁 with different browser and/or data
145 |
146 |
147 |
148 | Configurable retries with different data using the TestNG listener upon failure of the earlier data.
149 |
150 |
151 | Configurable retries with different browser using the TestNG listener upon failure of earlier browser.
152 |
153 |
154 |
155 | ```java
156 | # Retry Switch
157 | retry.data.switch = true
158 | retry.browser.switch = false
159 | ```
160 |
161 |
162 | ✅ Automated test data generation/selection for your CRUD operations
163 |
164 |
165 |
166 | Java Faker is used to generate random test data for most of your CREATE requests.
167 |
168 |
169 | You can also use ENUM for the master data obtained using the API requests from server.
170 |
171 |
172 | For all idempotent requests, the framework allows you to read data using API, Excel.
173 |
174 |
175 |
176 |
177 |
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 |
--------------------------------------------------------------------------------