├── .editorconfig
├── .gitignore
├── LICENSE
├── README.md
├── checkstyles
├── checkstyle-suppressions.xml
└── checkstyle.xml
├── pom.xml
├── src
├── main
│ ├── java
│ │ └── com
│ │ │ └── automate
│ │ │ ├── constants
│ │ │ ├── FrameworkConstants.java
│ │ │ └── StringConstants.java
│ │ │ ├── customannotations
│ │ │ └── FrameworkAnnotation.java
│ │ │ ├── customexceptions
│ │ │ ├── DriverInitializationException.java
│ │ │ ├── FrameworkException.java
│ │ │ ├── InvalidPathException.java
│ │ │ ├── JsonFileUsageException.java
│ │ │ └── PropertyFileUsageException.java
│ │ │ ├── driver
│ │ │ ├── Drivers.java
│ │ │ ├── factory
│ │ │ │ └── DriverFactory.java
│ │ │ └── manager
│ │ │ │ ├── DeviceManager.java
│ │ │ │ ├── DriverManager.java
│ │ │ │ └── PlatformManager.java
│ │ │ ├── entity
│ │ │ ├── LoginData.java
│ │ │ ├── SearchData.java
│ │ │ └── TestData.java
│ │ │ ├── enums
│ │ │ ├── CategoryType.java
│ │ │ ├── ConfigJson.java
│ │ │ ├── ConfigProperties.java
│ │ │ ├── MobileBrowserName.java
│ │ │ ├── MobileFindBy.java
│ │ │ ├── MobilePlatformName.java
│ │ │ └── WaitStrategy.java
│ │ │ ├── factories
│ │ │ └── WaitFactory.java
│ │ │ ├── listeners
│ │ │ ├── AnnotationTransformer.java
│ │ │ ├── Listeners.java
│ │ │ └── Retry.java
│ │ │ ├── pages
│ │ │ ├── GoogleSearchPage.java
│ │ │ ├── GoogleSearchResultPage.java
│ │ │ ├── LoginPage.java
│ │ │ ├── MenuPage.java
│ │ │ ├── ProductPage.java
│ │ │ ├── SettingsPage.java
│ │ │ └── screen
│ │ │ │ └── ScreenActions.java
│ │ │ ├── reports
│ │ │ ├── ExtentReportLogger.java
│ │ │ └── ExtentReportManager.java
│ │ │ └── utils
│ │ │ ├── AppiumServerManager.java
│ │ │ ├── DecodeUtils.java
│ │ │ ├── TestUtils.java
│ │ │ ├── configloader
│ │ │ ├── JsonUtils.java
│ │ │ └── PropertyUtils.java
│ │ │ ├── dataprovider
│ │ │ ├── DataProviderUtils.java
│ │ │ └── ExcelUtils.java
│ │ │ ├── screenrecording
│ │ │ ├── ScreenRecordingService.java
│ │ │ └── ScreenRecordingUtils.java
│ │ │ └── screenshot
│ │ │ ├── ScreenshotService.java
│ │ │ └── ScreenshotUtils.java
│ └── resources
│ │ └── log4j2.properties
└── test
│ ├── java
│ ├── base
│ │ └── BaseTest.java
│ └── com
│ │ └── tests
│ │ ├── GoogleTest.java
│ │ └── LoginTest.java
│ └── resources
│ ├── app
│ └── Android.SauceLabs.Mobile.Sample.app.2.7.1.apk
│ ├── config
│ ├── config.json
│ └── config.properties
│ ├── data
│ └── testdata.xlsx
│ └── executables
│ └── chromedriver.exe
├── testng_android_Emulator.xml
├── testng_android_ParallelExecution.xml
└── testng_android_RealDevice.xml
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | end_of_line = lf
6 | indent_size = 4
7 | indent_style = space
8 | insert_final_newline = true
9 | max_line_length = 120
10 | tab_width = 4
11 | ij_continuation_indent_size = 8
12 | ij_formatter_off_tag = @formatter:off
13 | ij_formatter_on_tag = @formatter:on
14 | ij_formatter_tags_enabled = false
15 | ij_smart_tabs = false
16 | ij_visual_guides = none
17 | ij_wrap_on_typing = false
18 |
19 | [*.java]
20 | indent_size = 2
21 | max_line_length = 136
22 | tab_width = 2
23 | ij_continuation_indent_size = 2
24 | ij_java_align_consecutive_assignments = false
25 | ij_java_align_consecutive_variable_declarations = false
26 | ij_java_align_group_field_declarations = false
27 | ij_java_align_multiline_annotation_parameters = false
28 | ij_java_align_multiline_array_initializer_expression = false
29 | ij_java_align_multiline_assignment = false
30 | ij_java_align_multiline_binary_operation = false
31 | ij_java_align_multiline_chained_methods = false
32 | ij_java_align_multiline_extends_list = false
33 | ij_java_align_multiline_for = true
34 | ij_java_align_multiline_method_parentheses = false
35 | ij_java_align_multiline_parameters = true
36 | ij_java_align_multiline_parameters_in_calls = true
37 | ij_java_align_multiline_parenthesized_expression = false
38 | ij_java_align_multiline_records = true
39 | ij_java_align_multiline_resources = true
40 | ij_java_align_multiline_ternary_operation = false
41 | ij_java_align_multiline_text_blocks = false
42 | ij_java_align_multiline_throws_list = false
43 | ij_java_align_subsequent_simple_methods = false
44 | ij_java_align_throws_keyword = false
45 | ij_java_annotation_parameter_wrap = off
46 | ij_java_array_initializer_new_line_after_left_brace = false
47 | ij_java_array_initializer_right_brace_on_new_line = false
48 | ij_java_array_initializer_wrap = normal
49 | ij_java_assert_statement_colon_on_next_line = false
50 | ij_java_assert_statement_wrap = normal
51 | ij_java_assignment_wrap = normal
52 | ij_java_binary_operation_sign_on_next_line = false
53 | ij_java_binary_operation_wrap = normal
54 | ij_java_blank_lines_after_anonymous_class_header = 0
55 | ij_java_blank_lines_after_class_header = 0
56 | ij_java_blank_lines_after_imports = 1
57 | ij_java_blank_lines_after_package = 1
58 | ij_java_blank_lines_around_class = 1
59 | ij_java_blank_lines_around_field = 0
60 | ij_java_blank_lines_around_field_in_interface = 0
61 | ij_java_blank_lines_around_initializer = 1
62 | ij_java_blank_lines_around_method = 1
63 | ij_java_blank_lines_around_method_in_interface = 1
64 | ij_java_blank_lines_before_class_end = 0
65 | ij_java_blank_lines_before_imports = 1
66 | ij_java_blank_lines_before_method_body = 0
67 | ij_java_blank_lines_before_package = 0
68 | ij_java_block_brace_style = end_of_line
69 | ij_java_block_comment_add_space = false
70 | ij_java_block_comment_at_first_column = true
71 | ij_java_builder_methods = none
72 | ij_java_call_parameters_new_line_after_left_paren = false
73 | ij_java_call_parameters_right_paren_on_new_line = false
74 | ij_java_call_parameters_wrap = normal
75 | ij_java_case_statement_on_separate_line = true
76 | ij_java_catch_on_new_line = false
77 | ij_java_class_annotation_wrap = split_into_lines
78 | ij_java_class_brace_style = end_of_line
79 | ij_java_class_count_to_use_import_on_demand = 999
80 | ij_java_class_names_in_javadoc = 1
81 | ij_java_do_not_indent_top_level_class_members = false
82 | ij_java_do_not_wrap_after_single_annotation = false
83 | ij_java_do_while_brace_force = never
84 | ij_java_doc_add_blank_line_after_description = true
85 | ij_java_doc_add_blank_line_after_param_comments = false
86 | ij_java_doc_add_blank_line_after_return = false
87 | ij_java_doc_add_p_tag_on_empty_lines = true
88 | ij_java_doc_align_exception_comments = true
89 | ij_java_doc_align_param_comments = true
90 | ij_java_doc_do_not_wrap_if_one_line = false
91 | ij_java_doc_enable_formatting = true
92 | ij_java_doc_enable_leading_asterisks = true
93 | ij_java_doc_indent_on_continuation = false
94 | ij_java_doc_keep_empty_lines = true
95 | ij_java_doc_keep_empty_parameter_tag = true
96 | ij_java_doc_keep_empty_return_tag = true
97 | ij_java_doc_keep_empty_throws_tag = true
98 | ij_java_doc_keep_invalid_tags = true
99 | ij_java_doc_param_description_on_new_line = false
100 | ij_java_doc_preserve_line_breaks = false
101 | ij_java_doc_use_throws_not_exception_tag = true
102 | ij_java_else_on_new_line = false
103 | ij_java_enum_constants_wrap = normal
104 | ij_java_extends_keyword_wrap = normal
105 | ij_java_extends_list_wrap = normal
106 | ij_java_field_annotation_wrap = split_into_lines
107 | ij_java_finally_on_new_line = false
108 | ij_java_for_brace_force = never
109 | ij_java_for_statement_new_line_after_left_paren = false
110 | ij_java_for_statement_right_paren_on_new_line = false
111 | ij_java_for_statement_wrap = normal
112 | ij_java_generate_final_locals = false
113 | ij_java_generate_final_parameters = false
114 | ij_java_if_brace_force = never
115 | ij_java_imports_layout = *, |, javax.**, java.**, |, $*
116 | ij_java_indent_case_from_switch = true
117 | ij_java_insert_inner_class_imports = false
118 | ij_java_insert_override_annotation = true
119 | ij_java_keep_blank_lines_before_right_brace = 2
120 | ij_java_keep_blank_lines_between_package_declaration_and_header = 2
121 | ij_java_keep_blank_lines_in_code = 2
122 | ij_java_keep_blank_lines_in_declarations = 2
123 | ij_java_keep_builder_methods_indents = false
124 | ij_java_keep_control_statement_in_one_line = true
125 | ij_java_keep_first_column_comment = true
126 | ij_java_keep_indents_on_empty_lines = false
127 | ij_java_keep_line_breaks = true
128 | ij_java_keep_multiple_expressions_in_one_line = false
129 | ij_java_keep_simple_blocks_in_one_line = false
130 | ij_java_keep_simple_classes_in_one_line = false
131 | ij_java_keep_simple_lambdas_in_one_line = false
132 | ij_java_keep_simple_methods_in_one_line = false
133 | ij_java_label_indent_absolute = false
134 | ij_java_label_indent_size = 0
135 | ij_java_lambda_brace_style = end_of_line
136 | ij_java_layout_static_imports_separately = true
137 | ij_java_line_comment_add_space = false
138 | ij_java_line_comment_at_first_column = true
139 | ij_java_method_annotation_wrap = split_into_lines
140 | ij_java_method_brace_style = end_of_line
141 | ij_java_method_call_chain_wrap = normal
142 | ij_java_method_parameters_new_line_after_left_paren = false
143 | ij_java_method_parameters_right_paren_on_new_line = false
144 | ij_java_method_parameters_wrap = normal
145 | ij_java_modifier_list_wrap = false
146 | ij_java_names_count_to_use_import_on_demand = 999
147 | ij_java_new_line_after_lparen_in_record_header = false
148 | ij_java_parameter_annotation_wrap = normal
149 | ij_java_parentheses_expression_new_line_after_left_paren = false
150 | ij_java_parentheses_expression_right_paren_on_new_line = false
151 | ij_java_place_assignment_sign_on_next_line = false
152 | ij_java_prefer_longer_names = true
153 | ij_java_prefer_parameters_wrap = false
154 | ij_java_record_components_wrap = normal
155 | ij_java_repeat_synchronized = true
156 | ij_java_replace_instanceof_and_cast = false
157 | ij_java_replace_null_check = true
158 | ij_java_replace_sum_lambda_with_method_ref = true
159 | ij_java_resource_list_new_line_after_left_paren = false
160 | ij_java_resource_list_right_paren_on_new_line = false
161 | ij_java_resource_list_wrap = normal
162 | ij_java_rparen_on_new_line_in_record_header = false
163 | ij_java_space_after_closing_angle_bracket_in_type_argument = false
164 | ij_java_space_after_colon = true
165 | ij_java_space_after_comma = true
166 | ij_java_space_after_comma_in_type_arguments = true
167 | ij_java_space_after_for_semicolon = true
168 | ij_java_space_after_quest = true
169 | ij_java_space_after_type_cast = true
170 | ij_java_space_before_annotation_array_initializer_left_brace = false
171 | ij_java_space_before_annotation_parameter_list = false
172 | ij_java_space_before_array_initializer_left_brace = true
173 | ij_java_space_before_catch_keyword = true
174 | ij_java_space_before_catch_left_brace = true
175 | ij_java_space_before_catch_parentheses = true
176 | ij_java_space_before_class_left_brace = true
177 | ij_java_space_before_colon = true
178 | ij_java_space_before_colon_in_foreach = true
179 | ij_java_space_before_comma = false
180 | ij_java_space_before_do_left_brace = true
181 | ij_java_space_before_else_keyword = true
182 | ij_java_space_before_else_left_brace = true
183 | ij_java_space_before_finally_keyword = true
184 | ij_java_space_before_finally_left_brace = true
185 | ij_java_space_before_for_left_brace = true
186 | ij_java_space_before_for_parentheses = true
187 | ij_java_space_before_for_semicolon = false
188 | ij_java_space_before_if_left_brace = true
189 | ij_java_space_before_if_parentheses = true
190 | ij_java_space_before_method_call_parentheses = false
191 | ij_java_space_before_method_left_brace = true
192 | ij_java_space_before_method_parentheses = false
193 | ij_java_space_before_opening_angle_bracket_in_type_parameter = false
194 | ij_java_space_before_quest = true
195 | ij_java_space_before_switch_left_brace = true
196 | ij_java_space_before_switch_parentheses = true
197 | ij_java_space_before_synchronized_left_brace = true
198 | ij_java_space_before_synchronized_parentheses = true
199 | ij_java_space_before_try_left_brace = true
200 | ij_java_space_before_try_parentheses = true
201 | ij_java_space_before_type_parameter_list = false
202 | ij_java_space_before_while_keyword = true
203 | ij_java_space_before_while_left_brace = true
204 | ij_java_space_before_while_parentheses = true
205 | ij_java_space_inside_one_line_enum_braces = false
206 | ij_java_space_within_empty_array_initializer_braces = false
207 | ij_java_space_within_empty_method_call_parentheses = false
208 | ij_java_space_within_empty_method_parentheses = false
209 | ij_java_spaces_around_additive_operators = true
210 | ij_java_spaces_around_assignment_operators = true
211 | ij_java_spaces_around_bitwise_operators = true
212 | ij_java_spaces_around_equality_operators = true
213 | ij_java_spaces_around_lambda_arrow = true
214 | ij_java_spaces_around_logical_operators = true
215 | ij_java_spaces_around_method_ref_dbl_colon = false
216 | ij_java_spaces_around_multiplicative_operators = true
217 | ij_java_spaces_around_relational_operators = true
218 | ij_java_spaces_around_shift_operators = true
219 | ij_java_spaces_around_type_bounds_in_type_parameters = true
220 | ij_java_spaces_around_unary_operator = false
221 | ij_java_spaces_within_angle_brackets = false
222 | ij_java_spaces_within_annotation_parentheses = false
223 | ij_java_spaces_within_array_initializer_braces = false
224 | ij_java_spaces_within_braces = false
225 | ij_java_spaces_within_brackets = false
226 | ij_java_spaces_within_cast_parentheses = false
227 | ij_java_spaces_within_catch_parentheses = false
228 | ij_java_spaces_within_for_parentheses = false
229 | ij_java_spaces_within_if_parentheses = false
230 | ij_java_spaces_within_method_call_parentheses = false
231 | ij_java_spaces_within_method_parentheses = false
232 | ij_java_spaces_within_parentheses = false
233 | ij_java_spaces_within_record_header = false
234 | ij_java_spaces_within_switch_parentheses = false
235 | ij_java_spaces_within_synchronized_parentheses = false
236 | ij_java_spaces_within_try_parentheses = false
237 | ij_java_spaces_within_while_parentheses = false
238 | ij_java_special_else_if_treatment = true
239 | ij_java_subclass_name_suffix = Impl
240 | ij_java_ternary_operation_signs_on_next_line = false
241 | ij_java_ternary_operation_wrap = normal
242 | ij_java_test_name_suffix = Test
243 | ij_java_throws_keyword_wrap = normal
244 | ij_java_throws_list_wrap = normal
245 | ij_java_use_external_annotations = false
246 | ij_java_use_fq_class_names = false
247 | ij_java_use_relative_indents = false
248 | ij_java_use_single_class_imports = true
249 | ij_java_variable_annotation_wrap = normal
250 | ij_java_visibility = public
251 | ij_java_while_brace_force = never
252 | ij_java_while_on_new_line = false
253 | ij_java_wrap_comments = false
254 | ij_java_wrap_first_method_in_call_chain = false
255 | ij_java_wrap_long_lines = false
256 |
257 | [*.properties]
258 | ij_properties_align_group_field_declarations = false
259 | ij_properties_keep_blank_lines = false
260 | ij_properties_key_value_delimiter = equals
261 | ij_properties_spaces_around_key_value_delimiter = false
262 |
263 | [.editorconfig]
264 | ij_editorconfig_align_group_field_declarations = false
265 | ij_editorconfig_space_after_colon = false
266 | ij_editorconfig_space_after_comma = true
267 | ij_editorconfig_space_before_colon = false
268 | ij_editorconfig_space_before_comma = false
269 | ij_editorconfig_spaces_around_assignment_operators = true
270 |
271 | [{*.ant,*.fxml,*.jhm,*.jnlp,*.jrxml,*.jspx,*.pom,*.rng,*.tagx,*.tld,*.wsdl,*.xml,*.xsd,*.xsl,*.xslt,*.xul}]
272 | ij_xml_align_attributes = true
273 | ij_xml_align_text = false
274 | ij_xml_attribute_wrap = normal
275 | ij_xml_block_comment_add_space = false
276 | ij_xml_block_comment_at_first_column = true
277 | ij_xml_keep_blank_lines = 2
278 | ij_xml_keep_indents_on_empty_lines = false
279 | ij_xml_keep_line_breaks = true
280 | ij_xml_keep_line_breaks_in_text = true
281 | ij_xml_keep_whitespaces = false
282 | ij_xml_keep_whitespaces_around_cdata = preserve
283 | ij_xml_keep_whitespaces_inside_cdata = false
284 | ij_xml_line_comment_at_first_column = true
285 | ij_xml_space_after_tag_name = false
286 | ij_xml_space_around_equals_in_attribute = false
287 | ij_xml_space_inside_empty_tag = false
288 | ij_xml_text_wrap = normal
289 | ij_xml_use_custom_settings = false
290 |
291 | [{*.bash,*.sh,*.zsh}]
292 | indent_size = 2
293 | tab_width = 2
294 | ij_shell_binary_ops_start_line = false
295 | ij_shell_keep_column_alignment_padding = false
296 | ij_shell_minify_program = false
297 | ij_shell_redirect_followed_by_space = false
298 | ij_shell_switch_cases_indented = false
299 | ij_shell_use_unix_line_separator = true
300 |
301 | [{*.har,*.json}]
302 | indent_size = 2
303 | ij_json_keep_blank_lines_in_code = 0
304 | ij_json_keep_indents_on_empty_lines = false
305 | ij_json_keep_line_breaks = true
306 | ij_json_space_after_colon = true
307 | ij_json_space_after_comma = true
308 | ij_json_space_before_colon = true
309 | ij_json_space_before_comma = false
310 | ij_json_spaces_within_braces = false
311 | ij_json_spaces_within_brackets = false
312 | ij_json_wrap_long_lines = false
313 |
314 | [{*.markdown,*.md}]
315 | ij_markdown_force_one_space_after_blockquote_symbol = true
316 | ij_markdown_force_one_space_after_header_symbol = true
317 | ij_markdown_force_one_space_after_list_bullet = true
318 | ij_markdown_force_one_space_between_words = true
319 | ij_markdown_keep_indents_on_empty_lines = false
320 | ij_markdown_max_lines_around_block_elements = 1
321 | ij_markdown_max_lines_around_header = 1
322 | ij_markdown_max_lines_between_paragraphs = 1
323 | ij_markdown_min_lines_around_block_elements = 1
324 | ij_markdown_min_lines_around_header = 1
325 | ij_markdown_min_lines_between_paragraphs = 1
326 |
327 | [{*.yaml,*.yml}]
328 | indent_size = 2
329 | ij_yaml_align_values_properties = do_not_align
330 | ij_yaml_autoinsert_sequence_marker = true
331 | ij_yaml_block_mapping_on_new_line = false
332 | ij_yaml_indent_sequence_value = true
333 | ij_yaml_keep_indents_on_empty_lines = false
334 | ij_yaml_keep_line_breaks = true
335 | ij_yaml_sequence_on_new_line = false
336 | ij_yaml_space_before_colon = false
337 | ij_yaml_spaces_within_braces = true
338 | ij_yaml_spaces_within_brackets = true
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /extent-test-report/
2 | /logs/
3 | /Screenshots/
4 | /screen-recordings/
5 | /server-logs/
6 | /target/
7 | /.idea/
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Thangaraj
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 | # Appium Mobile Automation Framework
2 |
3 | Framework for Mobile test automation (Native app and Browser) on Android and IOS devices :iphone:
4 |
5 |
6 |
7 | ## :rocket: Quick Start - Appium set up on Windows (Android):
8 |
9 | 1) Install [Java JDK8](https://www.oracle.com/java/technologies/javase/javase8-archive-downloads.html)
10 | and [IntelliJ IDEA](https://www.jetbrains.com/idea/download/)
11 | 2) Install [NodeJS](https://nodejs.org/en/download/)
12 | 3) Install [Android studio](https://developer.android.com/studio)
13 | 4) Install Appium Server using npm (CLI) command `npm install -g appium`. Appium server version 1.22.1
14 |
15 | ```
16 | Command to check the installed appium version: `appium --version`
17 | ```
18 |
19 | 5) Add below Android SDK path in the environment variable
20 |
21 | ```
22 | - ANDROID_HOME =
23 | - %ANDROID_HOME%\tools
24 | - %ANDROID_HOME%\tools\bin
25 | - %ANDROID_HOME%\platform-tools
26 | ```
27 |
28 | 6) Install [Appium desktop](https://github.com/appium/appium-desktop/releases/)
29 | 7) Install [Appium Inspector](https://github.com/appium/appium-inspector/releases)
30 |
31 | ## :rocket: Quick Start - Appium set up on MAC (Android):
32 |
33 | 1) Install Homebrew
34 | 2) Install [NodeJS](https://nodejs.org/en/download/)
35 | 3) Install [Java JDK8](https://www.oracle.com/java/technologies/javase/javase8-archive-downloads.html)
36 | and [IntelliJ IDEA](https://www.jetbrains.com/idea/download/)
37 | 4) Install Appium server using npm (CLI) or Appium desktop client
38 | 5) Install [Android studio](https://developer.android.com/studio)
39 | 6) Install [Appium Inspector](https://github.com/appium/appium-inspector/releases)
40 | 7) Set JAVA_HOME and ANDROID_HOME environment variables
41 |
42 | ## :pushpin: Appium Doctor to verify the installations
43 |
44 | 1) Install appium-doctor using command `npm install -g appium-doctor`
45 | 2) To view the list of available options `appium-doctor --help`
46 |
47 | ```
48 | To check Android set up `appium-doctor --android`
49 | To check ios set up `appium-doctor --ios`
50 | ```
51 |
52 | ## :pushpin: Creating Android Virtual Device (Emulator) from Android Studio:
53 |
54 | 1) Open Android Studio.
55 | 2) Click on Tools -> AVD Manager -> Create Virtual Device -> Select the device and OS version -> Finish.
56 | 3) Once Virtual device is created, click on Launch this AVD in the emulator.
57 | 4) Command to view the list of devices attached `adb devices`
58 |
59 | ## :pushpin: Android Real Device Set up:
60 |
61 | 1) Connect Android real device to the machine(Desktop/Laptop)
62 | 2) Turn on the developer options in android mobile
63 | 3) Enable USB debugging
64 | 4) Run command `adb devices` in cmd prompt to check whether the device is recognised
65 |
66 | ## :pushpin: Mirror android/ios device to your desktop
67 |
68 | 1) Download [Vysor](https://www.vysor.io/)
69 |
70 | ## :pushpin: Start Android Emulator from Command line
71 |
72 | 1) Open command prompt, go to ``
73 |
74 | ```
75 | Command to stard AVD: `emulator -avd `
76 | Command to stop/kill AVD: `adb -e emu kill`
77 | ```
78 |
79 | ## :pushpin: Pushing the App (.apk file) to Android Emulator:
80 |
81 | 1) Copy the .apk file and paste it in the path - ``
82 | 2) Open the cmd terminal from the directory where APK file is placed and enter command `adb install `
83 |
84 | ## :pushpin: Android - Finding appPackage and appActivity:
85 |
86 | If the app is already installed on your device then we can make use of appPackage and appActivity to launch the app
87 |
88 | Option 1 :
89 | 1) Open the app on the device, for which appPackage and appActivity is required.
90 | 2) Open powershell and enter command `adb shell dumpsys window | grep -E 'mCurrentFocus|mFocusedApp'`
91 | NOTE: This command may not work for newer Android OS (10 or 11). In that case, use command:
92 | `adb shell "dumpsys activity activities | grep mResumedActivity"`
93 |
94 | Option 2 :
95 | Install APK info app to retrieve appPackage and appActivity for the app installed in your device
96 |
97 | ## :pushpin: Inspecting Elements
98 |
99 | ### uiautomatorviewer
100 |
101 | 1) Go to the path - `\tools\bin\`
102 | 2) click on `uiautomatorviewer`
103 | 3) On the UI Automator Viewer, click on Device Screenshot (uiautomator dump). Ui automator will capture the screenshot
104 | of current open screen in the device.
105 |
106 |
107 |
108 | ### Appium Inspector
109 |
110 | 1) Start the Appium Server and connect with Real device/Emulator.
111 | 2) Open Appium Inspector app and provide the appium server details and Desired Capabilities.
112 |
113 |
114 |
115 | 3) Click on Start session which will start the appium inspector with layout shown below.
116 |
117 |
118 |
119 | ## :pushpin: Inspecting Element for mobile web browser
120 |
121 | ```
122 | Type url `chrome://inspect/#devices` in the desktop chrome browser and start inspecting element
123 | ```
124 |
125 |
126 |
127 | ## :pushpin: Launching Android Emulator Automatically
128 |
129 | Add below lines in the Desired capabilities
130 |
131 | ```
132 | capability.setCapability(AndroidMobileCapabilityType.AVD, "Pixel_3a");
133 | capability.setCapability(AndroidMobileCapabilityType.AVD_LAUNCH_TIMEOUT, "180000");
134 | ```
135 |
136 | ## :pushpin: Auto Discovery of compatible ChromeDriver
137 |
138 | Start appium server using command `appium --allow-insecure chromedriver_autodownload`
139 |
140 | ## :pushpin: Auto download of compatible ChromeDriver programmatically
141 |
142 | Add below line in the `AppiumServiceBuilder`
143 |
144 | ```
145 | AppiumServiceBuilder builder = new AppiumServiceBuilder();
146 | builder.withArgument(GeneralServerFlag.ALLOW_INSECURE, "chromedriver_autodownload");
147 | ```
148 |
149 | ## :pushpin: Start Appium server programmatically
150 |
151 | Use `AppiumServiceBuilder` and `AppiumDriverLocalService` to start the server programmatically Set environment
152 | variable `APPIUM_HOME = \node_modules\appium\build\lib` where `main.js` file is present
153 |
154 | ## :pushpin: Key Features
155 |
156 | :point_right: Supports Android and iOS Real Devices and Emulators.
157 |
158 | :point_right: Ability to start and stop the appium server on run-time. Configurable through `config.properties`
159 |
160 | :point_right: Supports capturing appium server logs on run-time.
161 |
162 | :point_right: Page object model design.
163 |
164 | :point_right: Supports parallel and sequential execution of tests.
165 |
166 | :point_right: Ability to capture screen(video) recording of tests on Android and iOS. Configurable
167 | through `config.properties`
168 |
169 | :point_right: Supports capturing screenshots for passed/failed/skipped steps which is configurable
170 | through `config.properties`
171 |
172 | :point_right: Ability to retry failed tests which is configurable through `config.properties`
173 |
174 | :point_right: Customised exception handling to provide the exceptions in a meaningful way.
175 |
176 | :point_right: Custom framework annotation to provide author name and category for each test.
177 |
178 | :point_right: Supports utilities to read test data from excel workbook and provides data to each test based on the test
179 | name.
180 |
181 | ## :pushpin: Running tests through Maven
182 |
183 | :point_right: Run test using command `mvn test -Dsurefire.suiteXmlFiles=`
184 |
185 | ## :pushpin: Running tests through testng xml
186 |
187 | :point_right: Create or Select the required testng xml -> Right click and select Run
188 |
189 | ## :pushpin: Custom Configurations in config.properties
190 |
191 |
192 |
193 | ## :pushpin: Report (Extent reports)
194 |
195 | 
196 |
197 | 
198 |
199 |
--------------------------------------------------------------------------------
/checkstyles/checkstyle-suppressions.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
8 |
9 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/checkstyles/checkstyle.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
4 | 4.0.0
5 | MobileAutomation
6 | AppiumMobileAutomationFramework
7 | 0.0.1-SNAPSHOT
8 |
9 |
10 | UTF-8
11 | 11
12 | 11
13 | 7.6.1
14 | 7.6.0
15 | 5.2.2
16 | 2.7.0
17 | 2.19.0
18 | 5.0.9
19 | 2.14.0
20 | 1.18.24
21 | 20220924
22 | 2.11.0
23 | 3.2.0
24 | 1.1.0
25 | 10.5.0
26 | checkstyles/checkstyle.xml
27 | checkstyles/checkstyle-suppressions.xml
28 |
29 |
30 |
31 |
32 | org.testng
33 | testng
34 | ${testng.version}
35 |
36 |
37 |
38 |
39 | io.appium
40 | java-client
41 | ${appium-java-client.version}
42 |
43 |
44 |
45 |
46 |
47 | org.apache.poi
48 | poi
49 | ${apache-poi.version}
50 |
51 |
52 |
53 |
54 | org.apache.poi
55 | poi-ooxml
56 | ${apache-poi.version}
57 |
58 |
59 |
60 |
61 | commons-io
62 | commons-io
63 | ${commons-io.version}
64 |
65 |
66 |
67 |
68 | com.aventstack
69 | extentreports
70 | ${extent-reports.version}
71 |
72 |
73 |
74 |
75 | com.jayway.jsonpath
76 | json-path
77 | ${json-path.version}
78 |
79 |
80 |
81 |
82 | org.json
83 | json
84 | ${json.version}
85 |
86 |
87 |
88 |
89 | org.apache.logging.log4j
90 | log4j-core
91 | ${log4j.version}
92 |
93 |
94 |
95 |
96 | org.apache.logging.log4j
97 | log4j-api
98 | ${log4j.version}
99 |
100 |
101 |
102 |
103 | com.fasterxml.jackson.core
104 | jackson-databind
105 | ${jackson.version}
106 |
107 |
108 |
109 |
110 | com.fasterxml.jackson.core
111 | jackson-core
112 | ${jackson.version}
113 |
114 |
115 |
116 |
117 | org.projectlombok
118 | lombok
119 | ${lombok.version}
120 |
121 |
122 |
123 | org.duraspace
124 | codestyle
125 | ${codestyle.version}
126 |
127 |
128 |
129 | com.puppycrawl.tools
130 | checkstyle
131 | ${checkstyle.version}
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 | org.apache.maven.plugins
140 | maven-compiler-plugin
141 | 3.10.1
142 |
143 |
144 |
145 | org.apache.maven.plugins
146 | maven-surefire-plugin
147 | 3.0.0-M5
148 |
149 | 0
150 |
151 |
152 | ${suiteXmlFile}
153 |
154 |
155 |
156 |
157 |
158 | org.apache.maven.plugins
159 | maven-checkstyle-plugin
160 | ${maven-checkstyle-plugin.version}
161 |
162 |
163 | verify-style
164 | verify
165 |
166 | check
167 |
168 |
169 |
170 |
171 | ${checkstyle.config}
172 | ${checkstyle.suppress}
173 | UTF-8
174 | UTF-8
175 | true
176 | true
177 | true
178 | true
179 |
180 |
181 |
182 |
183 |
184 |
--------------------------------------------------------------------------------
/src/main/java/com/automate/constants/FrameworkConstants.java:
--------------------------------------------------------------------------------
1 | package com.automate.constants;
2 |
3 | import com.automate.enums.ConfigProperties;
4 | import com.automate.utils.configloader.PropertyUtils;
5 | import lombok.AccessLevel;
6 | import lombok.NoArgsConstructor;
7 |
8 | import java.io.File;
9 | import java.time.LocalDateTime;
10 | import java.time.format.DateTimeFormatter;
11 |
12 | @NoArgsConstructor(access = AccessLevel.PRIVATE)
13 | public final class FrameworkConstants {
14 |
15 | public static final String PROJECT_PATH = System.getProperty("user.dir");
16 | public static final String TEST_RESOURCES_DIR = PROJECT_PATH + File.separator + "src/test/resources";
17 | public static final String ANDROID_APK_PATH =
18 | TEST_RESOURCES_DIR + File.separator + "app" + File.separator +
19 | "Android.SauceLabs.Mobile.Sample.app.2.7.1.apk";
20 | public static final String CHROME_DRIVER_EXE_PATH =
21 | TEST_RESOURCES_DIR + File.separator + "executables" + File.separator + "chromedriver.exe";
22 | public static final String CONFIG_PROPERTIES_PATH =
23 | TEST_RESOURCES_DIR + File.separator + "config" + File.separator + "config.properties";
24 | public static final String CONFIG_JSON_PATH =
25 | TEST_RESOURCES_DIR + File.separator + "config" + File.separator + "config.json";
26 | public static final String TEST_DATA_FILEPATH =
27 | TEST_RESOURCES_DIR + File.separator + "data" + File.separator + "testdata.xlsx";
28 | public static final String APPIUM_SERVER_HOST = "127.0.0.1";
29 | public static final int APPIUM_SERVER_PORT = 4723;
30 | public static final String APPIUM_JS_PATH = System.getenv("APPIUM_HOME") + File.separator + "main.js";
31 | public static final String CREDENTIALS_JSON = "data/credentials.json";
32 | public static final long EXPLICIT_WAIT = 15;
33 | public static final String TEST_DATA_SHEET = "TEST_DATA";
34 | public static final String IOS_APP_PATH = "";
35 | public static final String SCREENSHOT_PATH = PROJECT_PATH + File.separator + "screenshots";
36 | public static final String NODEJS_PATH = System.getenv("NODE_HOME") + File.separator + "node.exe";
37 |
38 | private static final String EXTENT_REPORT_PATH = PROJECT_PATH + File.separator + "extent-test-report";
39 | private static final String APPIUM_SERVER_LOGS_PATH = PROJECT_PATH + File.separator + "server-logs";
40 | private static final String SCREEN_RECORDING_PATH = PROJECT_PATH + File.separator + "screen-recordings";
41 |
42 | public static String getExtentReportPath() {
43 | if (PropertyUtils.getPropertyValue(ConfigProperties.OVERRIDE_REPORTS).equalsIgnoreCase("yes")) {
44 | return EXTENT_REPORT_PATH + File.separator + "index.html";
45 | } else {
46 | return EXTENT_REPORT_PATH + File.separator + getCurrentDateTime() + File.separator + "index.html";
47 | }
48 | }
49 |
50 | public static String getAppiumServerLogsPath() {
51 | if (PropertyUtils.getPropertyValue(ConfigProperties.OVERRIDE_SERVER_LOG).equalsIgnoreCase("yes")) {
52 | return APPIUM_SERVER_LOGS_PATH + File.separator + "server.log";
53 | } else {
54 | return APPIUM_SERVER_LOGS_PATH + File.separator + getCurrentDateTime() + File.separator + "server.log";
55 | }
56 | }
57 |
58 | public static String getScreenRecordingsPath() {
59 | File screenRecordingsDir = new File(SCREEN_RECORDING_PATH);
60 | if (!screenRecordingsDir.exists()) {
61 | screenRecordingsDir.mkdir();
62 | }
63 | return SCREEN_RECORDING_PATH;
64 | }
65 |
66 | private static String getCurrentDateTime() {
67 | DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy_MM_dd-HH_mm_ss");
68 | LocalDateTime localDateTime = LocalDateTime.now();
69 | return dateTimeFormatter.format(localDateTime);
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/src/main/java/com/automate/constants/StringConstants.java:
--------------------------------------------------------------------------------
1 | package com.automate.constants;
2 |
3 | import lombok.AccessLevel;
4 | import lombok.NoArgsConstructor;
5 |
6 | @NoArgsConstructor(access = AccessLevel.PRIVATE)
7 | public final class StringConstants {
8 |
9 | public static final String INVALID_LOGIN_ERROR_MESSAGE = "Username and password do not match any user in this service.";
10 | public static final String PRODUCT_PAGE_TITLE = "PRODUCTS";
11 | public static final String SEARCH_RESULTS_PAGE_TITLE = "Appium - Google Search";
12 | }
13 |
--------------------------------------------------------------------------------
/src/main/java/com/automate/customannotations/FrameworkAnnotation.java:
--------------------------------------------------------------------------------
1 | package com.automate.customannotations;
2 |
3 | import com.automate.enums.CategoryType;
4 |
5 | import java.lang.annotation.ElementType;
6 | import java.lang.annotation.Retention;
7 | import java.lang.annotation.RetentionPolicy;
8 | import java.lang.annotation.Target;
9 |
10 | @Retention(RetentionPolicy.RUNTIME)
11 | @Target(ElementType.METHOD)
12 | public @interface FrameworkAnnotation {
13 | String[] author();
14 |
15 | CategoryType[] category();
16 | }
17 |
--------------------------------------------------------------------------------
/src/main/java/com/automate/customexceptions/DriverInitializationException.java:
--------------------------------------------------------------------------------
1 | package com.automate.customexceptions;
2 |
3 | public class DriverInitializationException extends FrameworkException {
4 | public DriverInitializationException(String message) {
5 | super(message);
6 | }
7 |
8 | public DriverInitializationException(String message, Throwable t) {
9 | super(message, t);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/main/java/com/automate/customexceptions/FrameworkException.java:
--------------------------------------------------------------------------------
1 | package com.automate.customexceptions;
2 |
3 | public class FrameworkException extends RuntimeException {
4 |
5 | public FrameworkException(String message) {
6 | super(message);
7 | }
8 |
9 | public FrameworkException(String message, Throwable t) {
10 | super(message, t);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/main/java/com/automate/customexceptions/InvalidPathException.java:
--------------------------------------------------------------------------------
1 | package com.automate.customexceptions;
2 |
3 | public class InvalidPathException extends FrameworkException {
4 |
5 | public InvalidPathException(String message) {
6 | super(message);
7 | }
8 |
9 | public InvalidPathException(String message, Throwable t) {
10 | super(message, t);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/main/java/com/automate/customexceptions/JsonFileUsageException.java:
--------------------------------------------------------------------------------
1 | package com.automate.customexceptions;
2 |
3 | public class JsonFileUsageException extends FrameworkException {
4 |
5 | public JsonFileUsageException(String message) {
6 | super(message);
7 | }
8 |
9 | public JsonFileUsageException(String message, Throwable t) {
10 | super(message, t);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/main/java/com/automate/customexceptions/PropertyFileUsageException.java:
--------------------------------------------------------------------------------
1 | package com.automate.customexceptions;
2 |
3 | public class PropertyFileUsageException extends FrameworkException {
4 |
5 | public PropertyFileUsageException(String message) {
6 | super(message);
7 | }
8 |
9 | public PropertyFileUsageException(String message, Throwable t) {
10 | super(message, t);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/main/java/com/automate/driver/Drivers.java:
--------------------------------------------------------------------------------
1 | package com.automate.driver;
2 |
3 | import com.automate.constants.FrameworkConstants;
4 | import com.automate.customexceptions.DriverInitializationException;
5 | import com.automate.enums.ConfigJson;
6 | import com.automate.enums.MobileBrowserName;
7 | import io.appium.java_client.AppiumDriver;
8 | import io.appium.java_client.MobileElement;
9 | import io.appium.java_client.android.AndroidDriver;
10 | import io.appium.java_client.ios.IOSDriver;
11 | import io.appium.java_client.remote.AndroidMobileCapabilityType;
12 | import io.appium.java_client.remote.AutomationName;
13 | import io.appium.java_client.remote.IOSMobileCapabilityType;
14 | import io.appium.java_client.remote.MobileCapabilityType;
15 | import lombok.AccessLevel;
16 | import lombok.NoArgsConstructor;
17 | import org.openqa.selenium.Platform;
18 | import org.openqa.selenium.remote.CapabilityType;
19 | import org.openqa.selenium.remote.DesiredCapabilities;
20 |
21 | import java.net.URL;
22 |
23 | import static com.automate.utils.configloader.JsonUtils.getConfig;
24 |
25 | @NoArgsConstructor(access = AccessLevel.PRIVATE)
26 | public final class Drivers {
27 |
28 | public static AppiumDriver createAndroidDriverForNativeApp(String deviceName, String udid, int port, String emulator) {
29 | try {
30 | var capability = new DesiredCapabilities();
31 | capability.setCapability(CapabilityType.PLATFORM_NAME, Platform.ANDROID);
32 | capability.setCapability(MobileCapabilityType.DEVICE_NAME, deviceName);
33 | capability.setCapability(MobileCapabilityType.AUTOMATION_NAME, AutomationName.ANDROID_UIAUTOMATOR2); // Specific to Android
34 | capability.setCapability(MobileCapabilityType.UDID, udid); // To uniquely identify device
35 | capability.setCapability(MobileCapabilityType.APP, FrameworkConstants.ANDROID_APK_PATH);
36 | capability.setCapability(AndroidMobileCapabilityType.APP_PACKAGE, getConfig(ConfigJson.APP_PACKAGE));
37 | capability.setCapability(AndroidMobileCapabilityType.APP_ACTIVITY, getConfig(ConfigJson.APP_ACTIVITY));
38 | capability.setCapability(AndroidMobileCapabilityType.SYSTEM_PORT,
39 | port); // To set different port for each thread - This port is used to communicate with UiAutomator2
40 | if (emulator.equalsIgnoreCase("yes")) {
41 | capability.setCapability(AndroidMobileCapabilityType.AVD, deviceName);
42 | capability.setCapability(AndroidMobileCapabilityType.AVD_LAUNCH_TIMEOUT,
43 | Integer.parseInt(getConfig(ConfigJson.AVD_LAUNCH_TIMEOUT)));
44 | }
45 | return new AndroidDriver<>(new URL(getConfig(ConfigJson.APPIUM_URL)), capability);
46 | } catch (Exception e) {
47 | throw new DriverInitializationException("Failed to initialize driver. Please check the desired capabilities", e);
48 | }
49 | }
50 |
51 | public static AppiumDriver createAndroidDriverForWeb(String deviceName, String udid, int port, String emulator) {
52 | try {
53 | var capability = new DesiredCapabilities();
54 | capability.setCapability(CapabilityType.PLATFORM_NAME, Platform.ANDROID);
55 | capability.setCapability(MobileCapabilityType.DEVICE_NAME, deviceName);
56 | capability.setCapability(MobileCapabilityType.AUTOMATION_NAME, AutomationName.ANDROID_UIAUTOMATOR2);
57 | capability.setCapability(MobileCapabilityType.UDID, udid);
58 | capability.setCapability(CapabilityType.BROWSER_NAME, MobileBrowserName.CHROME);
59 | capability.setCapability(AndroidMobileCapabilityType.CHROMEDRIVER_PORT,
60 | port); // For Web view/Chrome browser to launch the browser on different port
61 | if (emulator.equalsIgnoreCase("yes")) {
62 | capability.setCapability(AndroidMobileCapabilityType.AVD, deviceName);
63 | capability.setCapability(AndroidMobileCapabilityType.AVD_LAUNCH_TIMEOUT,
64 | Integer.parseInt(getConfig(ConfigJson.AVD_LAUNCH_TIMEOUT)));
65 | }
66 |
67 | return new AndroidDriver<>(new URL(getConfig(ConfigJson.APPIUM_URL)), capability);
68 | } catch (Exception e) {
69 | throw new DriverInitializationException("Failed to initialize driver. Please check the desired capabilities", e);
70 | }
71 | }
72 |
73 | public static AppiumDriver createIOSDriverForNativeApp(String deviceName, String udid, int port) {
74 | try {
75 | var capability = new DesiredCapabilities();
76 | capability.setCapability(CapabilityType.PLATFORM_NAME, Platform.IOS);
77 | capability.setCapability(MobileCapabilityType.DEVICE_NAME, deviceName);
78 | capability.setCapability(MobileCapabilityType.AUTOMATION_NAME, AutomationName.IOS_XCUI_TEST);
79 | capability.setCapability(MobileCapabilityType.UDID, udid);
80 | capability.setCapability(MobileCapabilityType.APP, FrameworkConstants.IOS_APP_PATH);
81 | capability.setCapability(IOSMobileCapabilityType.BUNDLE_ID, getConfig(ConfigJson.BUNDLE_ID));
82 | capability.setCapability(IOSMobileCapabilityType.WDA_LOCAL_PORT,
83 | port); // To set different port for each thread - This port is used to communicate with WebDriverAgent driver
84 |
85 | return new IOSDriver<>(new URL(getConfig(ConfigJson.APPIUM_URL)), capability);
86 | } catch (Exception e) {
87 | throw new DriverInitializationException("Failed to initialize driver. Please check the desired capabilities", e);
88 | }
89 | }
90 |
91 | public static AppiumDriver createIOSDriverForWeb(String deviceName, String udid, int port) {
92 | try {
93 | var capability = new DesiredCapabilities();
94 | capability.setCapability(CapabilityType.PLATFORM_NAME, Platform.IOS);
95 | capability.setCapability(MobileCapabilityType.DEVICE_NAME, deviceName);
96 | capability.setCapability(MobileCapabilityType.AUTOMATION_NAME, AutomationName.IOS_XCUI_TEST);
97 | capability.setCapability(MobileCapabilityType.UDID, udid);
98 | capability.setCapability(IOSMobileCapabilityType.BUNDLE_ID, getConfig(ConfigJson.BUNDLE_ID));
99 | capability.setCapability(CapabilityType.BROWSER_NAME, MobileBrowserName.SAFARI);
100 | capability.setCapability("webkitDebugProxyPort", port); // For web view/Safari browser testing on real device
101 |
102 | return new IOSDriver<>(new URL(getConfig(ConfigJson.APPIUM_URL)), capability);
103 | } catch (Exception e) {
104 | throw new DriverInitializationException("Failed to initialize driver. Please check the desired capabilities", e);
105 | }
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/src/main/java/com/automate/driver/factory/DriverFactory.java:
--------------------------------------------------------------------------------
1 | package com.automate.driver.factory;
2 |
3 | import com.automate.customexceptions.DriverInitializationException;
4 | import com.automate.driver.Drivers;
5 | import com.automate.driver.manager.DriverManager;
6 | import com.automate.enums.MobilePlatformName;
7 | import io.appium.java_client.AppiumDriver;
8 | import io.appium.java_client.MobileElement;
9 | import lombok.AccessLevel;
10 | import lombok.NoArgsConstructor;
11 |
12 | import java.util.Objects;
13 |
14 | @NoArgsConstructor(access = AccessLevel.PRIVATE)
15 | public final class DriverFactory {
16 |
17 | public static void initializeDriver(MobilePlatformName mobilePlatformName, String deviceName, String udid, int port,
18 | String emulator) {
19 | AppiumDriver driver;
20 | switch (mobilePlatformName) {
21 | case ANDROID:
22 | driver = Drivers.createAndroidDriverForNativeApp(deviceName, udid, port, emulator);
23 | break;
24 | case ANDROID_WEB:
25 | driver = Drivers.createAndroidDriverForWeb(deviceName, udid, port, emulator);
26 | break;
27 | case IOS:
28 | driver = Drivers.createIOSDriverForNativeApp(deviceName, udid, port);
29 | break;
30 | case IOS_WEB:
31 | driver = Drivers.createIOSDriverForWeb(deviceName, udid, port);
32 | break;
33 | default:
34 | throw new DriverInitializationException(
35 | "Platform name " + mobilePlatformName + " is not found. Please check the platform name");
36 | }
37 | DriverManager.setAppiumDriver(driver);
38 | }
39 |
40 | public static void quitDriver() {
41 | if (Objects.nonNull(DriverManager.getDriver())) {
42 | DriverManager.getDriver().quit();
43 | DriverManager.unload();
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/main/java/com/automate/driver/manager/DeviceManager.java:
--------------------------------------------------------------------------------
1 | package com.automate.driver.manager;
2 |
3 | import lombok.AccessLevel;
4 | import lombok.NoArgsConstructor;
5 |
6 | @NoArgsConstructor(access = AccessLevel.PRIVATE)
7 | public final class DeviceManager {
8 |
9 | private static final ThreadLocal deviceName = new ThreadLocal<>();
10 |
11 | public static String getDeviceName() {
12 | return deviceName.get();
13 | }
14 |
15 | public static void setDeviceName(String device) {
16 | deviceName.set(device);
17 | }
18 |
19 | public static void unloadDeviceName() {
20 | deviceName.remove();
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/main/java/com/automate/driver/manager/DriverManager.java:
--------------------------------------------------------------------------------
1 | package com.automate.driver.manager;
2 |
3 | import io.appium.java_client.AppiumDriver;
4 | import io.appium.java_client.MobileElement;
5 | import lombok.AccessLevel;
6 | import lombok.NoArgsConstructor;
7 |
8 | import java.util.Objects;
9 |
10 | @NoArgsConstructor(access = AccessLevel.PRIVATE)
11 | public final class DriverManager {
12 |
13 | private static final ThreadLocal> threadLocalDriver = new ThreadLocal<>();
14 |
15 | public static AppiumDriver getDriver() {
16 | return threadLocalDriver.get();
17 | }
18 |
19 | public static void setAppiumDriver(AppiumDriver driver) {
20 | if (Objects.nonNull(driver))
21 | threadLocalDriver.set(driver);
22 | }
23 |
24 | public static void unload() {
25 | threadLocalDriver.remove();
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/main/java/com/automate/driver/manager/PlatformManager.java:
--------------------------------------------------------------------------------
1 | package com.automate.driver.manager;
2 |
3 | import lombok.AccessLevel;
4 | import lombok.NoArgsConstructor;
5 |
6 | @NoArgsConstructor(access = AccessLevel.PRIVATE)
7 | public class PlatformManager {
8 |
9 | private static final ThreadLocal platformName = new ThreadLocal<>();
10 |
11 | public static String getPlatformName() {
12 | return platformName.get();
13 | }
14 |
15 | public static void setPlatformName(String platform) {
16 | platformName.set(platform);
17 | }
18 |
19 | public static void unloadPlatformName() {
20 | platformName.remove();
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/main/java/com/automate/entity/LoginData.java:
--------------------------------------------------------------------------------
1 | package com.automate.entity;
2 |
3 | import lombok.Builder;
4 | import lombok.Getter;
5 |
6 | @Getter
7 | @Builder(setterPrefix = "set")
8 | public class LoginData {
9 |
10 | private String loginUsername;
11 | private String loginPassword;
12 | }
13 |
--------------------------------------------------------------------------------
/src/main/java/com/automate/entity/SearchData.java:
--------------------------------------------------------------------------------
1 | package com.automate.entity;
2 |
3 | import lombok.Builder;
4 | import lombok.Getter;
5 |
6 | @Getter
7 | @Builder(setterPrefix = "set")
8 | public class SearchData {
9 |
10 | private String searchText;
11 | }
12 |
--------------------------------------------------------------------------------
/src/main/java/com/automate/entity/TestData.java:
--------------------------------------------------------------------------------
1 | package com.automate.entity;
2 |
3 | import lombok.Builder;
4 | import lombok.Getter;
5 |
6 | @Getter
7 | @Builder(setterPrefix = "set")
8 | public class TestData {
9 |
10 | private LoginData loginData;
11 | private SearchData searchData;
12 | }
13 |
--------------------------------------------------------------------------------
/src/main/java/com/automate/enums/CategoryType.java:
--------------------------------------------------------------------------------
1 | package com.automate.enums;
2 |
3 | public enum CategoryType {
4 |
5 | REGRESSION, SANITY, SMOKE
6 | }
7 |
--------------------------------------------------------------------------------
/src/main/java/com/automate/enums/ConfigJson.java:
--------------------------------------------------------------------------------
1 | package com.automate.enums;
2 |
3 | public enum ConfigJson {
4 | APP_ACTIVITY, APP_PACKAGE, APPIUM_URL, AVD_LAUNCH_TIMEOUT,
5 | BUNDLE_ID,
6 | URL
7 | }
8 |
--------------------------------------------------------------------------------
/src/main/java/com/automate/enums/ConfigProperties.java:
--------------------------------------------------------------------------------
1 | package com.automate.enums;
2 |
3 | public enum ConfigProperties {
4 | RECORD_SCREEN, START_APPIUM_SERVER,
5 | OVERRIDE_REPORTS, PASSED_STEP_SCREENSHOTS, FAILED_STEP_SCREENSHOTS, SKIPPED_STEP_SCREENSHOTS,
6 | RETRY_FAILED_TESTS, RETRY_COUNT,
7 | OVERRIDE_SERVER_LOG
8 | }
9 |
--------------------------------------------------------------------------------
/src/main/java/com/automate/enums/MobileBrowserName.java:
--------------------------------------------------------------------------------
1 | package com.automate.enums;
2 |
3 | public enum MobileBrowserName {
4 | CHROME,
5 | SAFARI
6 | }
7 |
--------------------------------------------------------------------------------
/src/main/java/com/automate/enums/MobileFindBy.java:
--------------------------------------------------------------------------------
1 | package com.automate.enums;
2 |
3 | public enum MobileFindBy {
4 | XPATH, CSS, ID, NAME, CLASS, ACCESSIBILITY_ID
5 | }
6 |
--------------------------------------------------------------------------------
/src/main/java/com/automate/enums/MobilePlatformName.java:
--------------------------------------------------------------------------------
1 | package com.automate.enums;
2 |
3 | public enum MobilePlatformName {
4 |
5 | ANDROID,
6 | ANDROID_WEB,
7 | IOS,
8 | IOS_WEB
9 | }
10 |
--------------------------------------------------------------------------------
/src/main/java/com/automate/enums/WaitStrategy.java:
--------------------------------------------------------------------------------
1 | package com.automate.enums;
2 |
3 | public enum WaitStrategy {
4 | CLICKABLE, PRESENCE, VISIBLE, NONE
5 | }
6 |
--------------------------------------------------------------------------------
/src/main/java/com/automate/factories/WaitFactory.java:
--------------------------------------------------------------------------------
1 | package com.automate.factories;
2 |
3 | import com.automate.constants.FrameworkConstants;
4 | import com.automate.driver.manager.DriverManager;
5 | import com.automate.enums.WaitStrategy;
6 | import io.appium.java_client.MobileElement;
7 | import lombok.AccessLevel;
8 | import lombok.NoArgsConstructor;
9 | import org.openqa.selenium.By;
10 | import org.openqa.selenium.WebElement;
11 | import org.openqa.selenium.support.ui.ExpectedConditions;
12 | import org.openqa.selenium.support.ui.WebDriverWait;
13 |
14 | import java.util.EnumMap;
15 | import java.util.Map;
16 | import java.util.function.Function;
17 |
18 | import static com.automate.enums.WaitStrategy.CLICKABLE;
19 | import static com.automate.enums.WaitStrategy.NONE;
20 | import static com.automate.enums.WaitStrategy.PRESENCE;
21 | import static com.automate.enums.WaitStrategy.VISIBLE;
22 |
23 | @NoArgsConstructor(access = AccessLevel.PRIVATE)
24 | public final class WaitFactory {
25 |
26 | private static final Map> WAIT_FOR_ELEMENT_FUNCTION_MAP =
27 | new EnumMap<>(WaitStrategy.class);
28 |
29 | private static final Function CLICKABLE_ELEMENT = mobileElement ->
30 | new WebDriverWait(DriverManager.getDriver(), FrameworkConstants.EXPLICIT_WAIT)
31 | .until(ExpectedConditions.elementToBeClickable(mobileElement));
32 | private static final Function VISIBILITY_OF_ELEMENT = mobileElement ->
33 | new WebDriverWait(DriverManager.getDriver(), FrameworkConstants.EXPLICIT_WAIT)
34 | .until(ExpectedConditions.visibilityOf(mobileElement));
35 | private static final Function NO_MATCH = mobileElement -> mobileElement;
36 |
37 | private static final Map> WAIT_FOR_ELEMENT_LOCATED_BY_FUNCTION_MAP =
38 | new EnumMap<>(WaitStrategy.class);
39 |
40 | private static final Function CLICKABLE_ELEMENT_BY = by ->
41 | new WebDriverWait(DriverManager.getDriver(), FrameworkConstants.EXPLICIT_WAIT)
42 | .until(ExpectedConditions.elementToBeClickable(by));
43 | private static final Function PRESENCE_OF_ELEMENT_BY = by ->
44 | new WebDriverWait(DriverManager.getDriver(), FrameworkConstants.EXPLICIT_WAIT)
45 | .until(ExpectedConditions.presenceOfElementLocated(by));
46 | private static final Function VISIBILITY_OF_ELEMENT_BY = by ->
47 | new WebDriverWait(DriverManager.getDriver(), FrameworkConstants.EXPLICIT_WAIT)
48 | .until(ExpectedConditions.visibilityOfElementLocated(by));
49 | private static final Function NO_MATCH_BY = by -> DriverManager.getDriver().findElement(by);
50 |
51 | static {
52 | WAIT_FOR_ELEMENT_FUNCTION_MAP.put(CLICKABLE, CLICKABLE_ELEMENT);
53 | WAIT_FOR_ELEMENT_FUNCTION_MAP.put(VISIBLE, VISIBILITY_OF_ELEMENT);
54 | WAIT_FOR_ELEMENT_FUNCTION_MAP.put(NONE, NO_MATCH);
55 | WAIT_FOR_ELEMENT_LOCATED_BY_FUNCTION_MAP.put(CLICKABLE, CLICKABLE_ELEMENT_BY);
56 | WAIT_FOR_ELEMENT_LOCATED_BY_FUNCTION_MAP.put(PRESENCE, PRESENCE_OF_ELEMENT_BY);
57 | WAIT_FOR_ELEMENT_LOCATED_BY_FUNCTION_MAP.put(VISIBLE, VISIBILITY_OF_ELEMENT_BY);
58 | WAIT_FOR_ELEMENT_LOCATED_BY_FUNCTION_MAP.put(NONE, NO_MATCH_BY);
59 | }
60 |
61 | public static WebElement explicitlyWaitForElementLocatedBy(WaitStrategy waitStrategy, By by) {
62 | return WAIT_FOR_ELEMENT_LOCATED_BY_FUNCTION_MAP.get(waitStrategy).apply(by);
63 | }
64 |
65 | public static WebElement explicitlyWaitForElement(WaitStrategy waitStrategy, MobileElement mobileElement) {
66 | return WAIT_FOR_ELEMENT_FUNCTION_MAP.get(waitStrategy).apply(mobileElement);
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/src/main/java/com/automate/listeners/AnnotationTransformer.java:
--------------------------------------------------------------------------------
1 | package com.automate.listeners;
2 |
3 | import com.automate.utils.dataprovider.DataProviderUtils;
4 | import org.testng.IAnnotationTransformer;
5 | import org.testng.annotations.ITestAnnotation;
6 |
7 | import java.lang.reflect.Constructor;
8 | import java.lang.reflect.Method;
9 |
10 | public class AnnotationTransformer implements IAnnotationTransformer {
11 |
12 | @Override
13 | public void transform(ITestAnnotation annotation, Class testClass, Constructor testConstructor, Method testMethod) {
14 | annotation.setDataProvider("getData");
15 | annotation.setDataProviderClass(DataProviderUtils.class);
16 | annotation.setRetryAnalyzer(Retry.class);
17 | }
18 | }
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/src/main/java/com/automate/listeners/Listeners.java:
--------------------------------------------------------------------------------
1 | package com.automate.listeners;
2 |
3 | import com.automate.customannotations.FrameworkAnnotation;
4 | import com.automate.reports.ExtentReportLogger;
5 | import com.automate.reports.ExtentReportManager;
6 | import org.testng.ISuite;
7 | import org.testng.ISuiteListener;
8 | import org.testng.ITestContext;
9 | import org.testng.ITestListener;
10 | import org.testng.ITestResult;
11 |
12 | public class Listeners implements ITestListener, ISuiteListener {
13 |
14 | @Override
15 | public void onStart(ISuite suite) {
16 | ExtentReportManager.initExtentReport();
17 | }
18 |
19 | @Override
20 | public void onTestStart(ITestResult result) {
21 | ExtentReportManager.createTest(result.getMethod().getMethodName());
22 | ExtentReportManager.addAuthors(
23 | result.getMethod().getConstructorOrMethod().getMethod().getAnnotation(FrameworkAnnotation.class).author());
24 | ExtentReportManager.addCategories(
25 | result.getMethod().getConstructorOrMethod().getMethod().getAnnotation(FrameworkAnnotation.class).category());
26 | ExtentReportManager.addDevices();
27 | ExtentReportLogger.logInfo("Test - " + result.getMethod().getMethodName() + " is started");
28 | }
29 |
30 | @Override
31 | public void onTestSuccess(ITestResult result) {
32 | ExtentReportLogger.logPass("Test - " + result.getMethod().getMethodName() + " is passed");
33 | }
34 |
35 | @Override
36 | public void onTestFailure(ITestResult result) {
37 | ExtentReportLogger.logFail("Test - " + result.getMethod().getMethodName() + " is failed", result.getThrowable());
38 | }
39 |
40 | @Override
41 | public void onTestSkipped(ITestResult result) {
42 | ExtentReportLogger.logSkip("Test - " + result.getMethod().getMethodName() + " is skipped");
43 | }
44 |
45 | @Override
46 | public void onFinish(ISuite suite) {
47 | ExtentReportManager.flushExtentReport();
48 | }
49 |
50 | @Override
51 | public void onTestFailedButWithinSuccessPercentage(ITestResult result) {
52 | // No implementation
53 | }
54 |
55 | @Override
56 | public void onStart(ITestContext iTestContext) {
57 | // No implementation
58 | }
59 |
60 | @Override
61 | public void onFinish(ITestContext iTestContext) {
62 | // No implementation
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/src/main/java/com/automate/listeners/Retry.java:
--------------------------------------------------------------------------------
1 | package com.automate.listeners;
2 |
3 | import com.automate.enums.ConfigProperties;
4 | import com.automate.utils.configloader.PropertyUtils;
5 | import org.testng.IRetryAnalyzer;
6 | import org.testng.ITestResult;
7 |
8 | public class Retry implements IRetryAnalyzer {
9 |
10 | private final int maxRetry = Integer.parseInt(PropertyUtils.getPropertyValue(ConfigProperties.RETRY_COUNT));
11 | private int count = 0;
12 |
13 | @Override
14 | public boolean retry(ITestResult result) {
15 | boolean value = false;
16 | if (PropertyUtils.getPropertyValue(ConfigProperties.RETRY_FAILED_TESTS).equalsIgnoreCase("yes")) {
17 | value = count < maxRetry;
18 | count++;
19 | }
20 | return value;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/main/java/com/automate/pages/GoogleSearchPage.java:
--------------------------------------------------------------------------------
1 | package com.automate.pages;
2 |
3 | import com.automate.pages.screen.ScreenActions;
4 | import io.appium.java_client.MobileElement;
5 | import org.openqa.selenium.support.FindBy;
6 |
7 | public final class GoogleSearchPage extends ScreenActions {
8 |
9 | @FindBy(xpath = "//input[@aria-label='Search']")
10 | private static MobileElement txtFieldSearch;
11 |
12 | public GoogleSearchResultPage performSearch(String searchText) {
13 | enterValueAndPressEnter(txtFieldSearch, searchText, "Search text box");
14 | return new GoogleSearchResultPage();
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/main/java/com/automate/pages/GoogleSearchResultPage.java:
--------------------------------------------------------------------------------
1 | package com.automate.pages;
2 |
3 | import com.automate.driver.manager.DriverManager;
4 | import com.automate.pages.screen.ScreenActions;
5 |
6 | public final class GoogleSearchResultPage extends ScreenActions {
7 |
8 | public String getSearchResultsPageTitle() {
9 | return DriverManager.getDriver().getTitle();
10 | }
11 |
12 | }
13 |
--------------------------------------------------------------------------------
/src/main/java/com/automate/pages/LoginPage.java:
--------------------------------------------------------------------------------
1 | package com.automate.pages;
2 |
3 | import com.automate.entity.LoginData;
4 | import com.automate.enums.WaitStrategy;
5 | import com.automate.pages.screen.ScreenActions;
6 | import io.appium.java_client.MobileElement;
7 | import io.appium.java_client.pagefactory.AndroidFindBy;
8 | import io.appium.java_client.pagefactory.iOSXCUITFindBy;
9 |
10 | public final class LoginPage extends ScreenActions {
11 |
12 | @AndroidFindBy(accessibility = "test-Username")
13 | @iOSXCUITFindBy(accessibility = "test-Username")
14 | private static MobileElement txtFieldUsername;
15 |
16 | @AndroidFindBy(accessibility = "test-Password")
17 | @iOSXCUITFindBy(accessibility = "test-Password")
18 | private static MobileElement txtFieldPassword;
19 |
20 | @AndroidFindBy(accessibility = "test-LOGIN")
21 | @iOSXCUITFindBy(accessibility = "test-LOGIN")
22 | private static MobileElement btnLogin;
23 |
24 | @AndroidFindBy(xpath = "//android.view.ViewGroup[@content-desc='test-Error message']/android.widget.TextView")
25 | private static MobileElement errorMessage;
26 |
27 | public boolean isLoginPageDisplayed() {
28 | return isElementDisplayed(txtFieldUsername);
29 | }
30 |
31 | public LoginPage setUsername(String username) {
32 | enter(txtFieldUsername, username, "Username");
33 | return this;
34 | }
35 |
36 | public LoginPage setPassword(String password) {
37 | enter(txtFieldPassword, password, "Password");
38 | return this;
39 | }
40 |
41 | public ProductPage tapOnLogin() {
42 | click(btnLogin, "Login");
43 | return new ProductPage();
44 | }
45 |
46 | public ProductPage login(LoginData loginData) {
47 | setUsername(loginData.getLoginUsername())
48 | .setPassword(loginData.getLoginPassword())
49 | .tapOnLogin();
50 |
51 | return new ProductPage();
52 | }
53 |
54 | public String getErrorText() {
55 | return getText(errorMessage, WaitStrategy.VISIBLE);
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/main/java/com/automate/pages/MenuPage.java:
--------------------------------------------------------------------------------
1 | package com.automate.pages;
2 |
3 | import com.automate.pages.screen.ScreenActions;
4 | import io.appium.java_client.MobileElement;
5 | import io.appium.java_client.pagefactory.AndroidFindBy;
6 |
7 | public final class MenuPage extends ScreenActions {
8 |
9 | @AndroidFindBy(xpath = "//android.view.ViewGroup[@content-desc=\"test-Menu\"]/android.view.ViewGroup/android.widget.ImageView")
10 | private static MobileElement menuIcon;
11 |
12 | public SettingsPage pressMenuIcon() {
13 | click(menuIcon, "Menu icon");
14 | return new SettingsPage();
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/main/java/com/automate/pages/ProductPage.java:
--------------------------------------------------------------------------------
1 | package com.automate.pages;
2 |
3 | import com.automate.enums.WaitStrategy;
4 | import com.automate.pages.screen.ScreenActions;
5 | import io.appium.java_client.MobileElement;
6 | import io.appium.java_client.pagefactory.AndroidFindBy;
7 |
8 | public final class ProductPage extends ScreenActions {
9 |
10 | @AndroidFindBy(xpath = "//android.view.ViewGroup[@content-desc=\"test-Cart drop zone\"]/android.view.ViewGroup/android.widget.TextView")
11 | private static MobileElement productPageTitle;
12 |
13 | public String getProductPageTitle() {
14 | return getText(productPageTitle, WaitStrategy.VISIBLE);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/main/java/com/automate/pages/SettingsPage.java:
--------------------------------------------------------------------------------
1 | package com.automate.pages;
2 |
3 | import com.automate.pages.screen.ScreenActions;
4 | import io.appium.java_client.MobileElement;
5 | import io.appium.java_client.pagefactory.AndroidFindBy;
6 |
7 | public final class SettingsPage extends ScreenActions {
8 |
9 | @AndroidFindBy(accessibility = "test-LOGOUT")
10 | private static MobileElement logOutButton;
11 |
12 | public LoginPage pressLogOutButton() {
13 | click(logOutButton, "Logout");
14 | return new LoginPage();
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/main/java/com/automate/pages/screen/ScreenActions.java:
--------------------------------------------------------------------------------
1 | package com.automate.pages.screen;
2 |
3 | import com.automate.driver.manager.DriverManager;
4 | import com.automate.enums.MobileFindBy;
5 | import com.automate.enums.WaitStrategy;
6 | import com.automate.reports.ExtentReportLogger;
7 | import com.google.common.collect.ImmutableMap;
8 | import com.google.common.collect.Ordering;
9 | import io.appium.java_client.MobileElement;
10 | import io.appium.java_client.MultiTouchAction;
11 | import io.appium.java_client.TouchAction;
12 | import io.appium.java_client.android.AndroidDriver;
13 | import io.appium.java_client.android.PowerACState;
14 | import io.appium.java_client.pagefactory.AppiumFieldDecorator;
15 | import io.appium.java_client.touch.TapOptions;
16 | import io.appium.java_client.touch.WaitOptions;
17 | import io.appium.java_client.touch.offset.ElementOption;
18 | import io.appium.java_client.touch.offset.PointOption;
19 | import org.openqa.selenium.By;
20 | import org.openqa.selenium.Dimension;
21 | import org.openqa.selenium.Keys;
22 | import org.openqa.selenium.ScreenOrientation;
23 | import org.openqa.selenium.WebElement;
24 | import org.openqa.selenium.interactions.Actions;
25 | import org.openqa.selenium.interactions.touch.TouchActions;
26 | import org.openqa.selenium.support.PageFactory;
27 |
28 | import java.time.Duration;
29 | import java.util.EnumMap;
30 | import java.util.HashMap;
31 | import java.util.List;
32 | import java.util.Map;
33 | import java.util.concurrent.TimeUnit;
34 | import java.util.function.Function;
35 |
36 | import static com.automate.enums.MobileFindBy.ACCESSIBILITY_ID;
37 | import static com.automate.enums.MobileFindBy.CLASS;
38 | import static com.automate.enums.MobileFindBy.CSS;
39 | import static com.automate.enums.MobileFindBy.ID;
40 | import static com.automate.enums.MobileFindBy.NAME;
41 | import static com.automate.enums.MobileFindBy.XPATH;
42 | import static com.automate.factories.WaitFactory.explicitlyWaitForElement;
43 |
44 | public class ScreenActions {
45 |
46 | private final Map> mobileFindByFunctionMap = new EnumMap<>(MobileFindBy.class);
47 | private final Function findByXpath =
48 | mobileElement -> DriverManager.getDriver().findElementByXPath(mobileElement);
49 | private final Function findByCss =
50 | mobileElement -> DriverManager.getDriver().findElementByCssSelector(mobileElement);
51 | private final Function findById = mobileElement -> DriverManager.getDriver().findElementById(mobileElement);
52 | private final Function findByName =
53 | mobileElement -> DriverManager.getDriver().findElementByName(mobileElement);
54 | private final Function findByAccessibilityId =
55 | mobileElement -> DriverManager.getDriver().findElementByAccessibilityId(mobileElement);
56 | private final Function findByClassName =
57 | mobileElement -> DriverManager.getDriver().findElementByClassName(mobileElement);
58 | protected ScreenActions() {
59 | PageFactory.initElements(new AppiumFieldDecorator(DriverManager.getDriver()), this);
60 | }
61 |
62 | private MobileElement getMobileElement(String mobileElement, MobileFindBy mobileFindBy) {
63 | if (mobileFindByFunctionMap.isEmpty()) {
64 | mobileFindByFunctionMap.put(XPATH, findByXpath);
65 | mobileFindByFunctionMap.put(CSS, findByCss);
66 | mobileFindByFunctionMap.put(ID, findById);
67 | mobileFindByFunctionMap.put(NAME, findByName);
68 | mobileFindByFunctionMap.put(ACCESSIBILITY_ID, findByAccessibilityId);
69 | mobileFindByFunctionMap.put(CLASS, findByClassName);
70 | }
71 | return mobileFindByFunctionMap.get(mobileFindBy).apply(mobileElement);
72 | }
73 |
74 | protected MobileElement getDynamicMobileElement(String mobileElement, MobileFindBy mobileFindBy) {
75 | if (mobileFindBy == XPATH) {
76 | return DriverManager.getDriver().findElement(By.xpath(mobileElement));
77 | } else if (mobileFindBy == MobileFindBy.CSS) {
78 | return DriverManager.getDriver().findElement(By.cssSelector(mobileElement));
79 | }
80 | return null;
81 | }
82 |
83 | protected void waitForPageLoad(int waitTime) {
84 | DriverManager.getDriver().manage().timeouts().pageLoadTimeout(waitTime, TimeUnit.SECONDS);
85 | }
86 |
87 | protected String getTextFromAttribute(WaitStrategy waitStrategy, MobileElement element) {
88 | return explicitlyWaitForElement(waitStrategy, element).getAttribute("text");
89 | }
90 |
91 | protected String getText(MobileElement element, WaitStrategy waitStrategy) {
92 | return explicitlyWaitForElement(waitStrategy, element).getText();
93 | }
94 |
95 | protected boolean isElementDisplayed(MobileElement element) {
96 | return element.isDisplayed();
97 | }
98 |
99 | protected void doClear(MobileElement element) {
100 | element.clear();
101 | }
102 |
103 | protected void getServerStatus() {
104 | DriverManager.getDriver().getStatus();
105 | }
106 |
107 | protected void setOrientation(ScreenOrientation screenOrientationType) {
108 | switch (screenOrientationType) {
109 | case LANDSCAPE:
110 | DriverManager.getDriver().rotate(ScreenOrientation.LANDSCAPE);
111 | ExtentReportLogger.logInfo("Device Orientation is set to Landscape");
112 | break;
113 | case PORTRAIT:
114 | DriverManager.getDriver().rotate(ScreenOrientation.PORTRAIT);
115 | ExtentReportLogger.logInfo("Device Orientation is set to Portrait");
116 | break;
117 | default:
118 | throw new IllegalStateException("Unexpected value in Screen Orientation: " + screenOrientationType);
119 | }
120 | }
121 |
122 | protected void backgroundApp() {
123 | DriverManager.getDriver().runAppInBackground(Duration.ofSeconds(10));
124 | }
125 |
126 | protected String getElementAttribute(MobileElement element, String attributeName) {
127 | return element.getAttribute(attributeName);
128 | }
129 |
130 | protected boolean isElementSelected(MobileElement element) {
131 | return element.isSelected();
132 | }
133 |
134 | protected boolean isElementEnabled(MobileElement element) {
135 | return element.isEnabled();
136 | }
137 |
138 | protected WebElement getActiveElement() {
139 | return DriverManager.getDriver().switchTo().activeElement();
140 | }
141 |
142 | protected void moveMouseToElement(WebElement element, int xoffset, int yoffset) {
143 | new Actions(DriverManager.getDriver())
144 | .moveToElement(element, xoffset, yoffset)
145 | .perform();
146 | ExtentReportLogger.logInfo("Move to target element :" + element);
147 | }
148 |
149 | protected void doubleClickOnElement(WebElement element) {
150 | new Actions(DriverManager.getDriver())
151 | .moveToElement(element)
152 | .doubleClick()
153 | .perform();
154 | ExtentReportLogger.logInfo("Double click on element : " + element);
155 | }
156 |
157 | protected void performSingleTap(WebElement element) {
158 | new TouchActions(DriverManager.getDriver())
159 | .singleTap(element)
160 | .perform();
161 | ExtentReportLogger.logInfo("Single tap on element : " + element);
162 | }
163 |
164 | protected void performDoubleTap(WebElement element) {
165 | new TouchActions(DriverManager.getDriver())
166 | .doubleTap(element)
167 | .perform();
168 | ExtentReportLogger.logInfo("Double tap on element : " + element);
169 | }
170 |
171 | protected void performLongTap(WebElement element) {
172 | new TouchActions(DriverManager.getDriver())
173 | .longPress(element)
174 | .perform();
175 | ExtentReportLogger.logInfo("Long press on element : " + element);
176 | }
177 |
178 | protected void touchScreenScroll(WebElement element, int x, int y) {
179 | new TouchActions(DriverManager.getDriver())
180 | .scroll(element, x, y)
181 | .perform();
182 | }
183 |
184 | protected void hideKeyboard() {
185 | DriverManager.getDriver().hideKeyboard();
186 | }
187 |
188 | protected void scrollClickAndroid(String scrollableListId, String selectionText) {
189 | ((AndroidDriver) DriverManager.getDriver()).findElementByAndroidUIAutomator(
190 | "new UiScrollable(new UiSelector().scrollable(true)."
191 | + "resourceId(\"" + scrollableListId + "\"))"
192 | + ".setAsHorizontalList().scrollIntoView(new UiSelector().text(\"" + selectionText + "\"))").click();
193 | }
194 |
195 | protected void click(MobileElement element, String elementName) {
196 | try {
197 | element.click();
198 | ExtentReportLogger.logInfo("Clicked on " + elementName);
199 | } catch (Exception e) {
200 | ExtentReportLogger.logFail("Exception occurred when clicking on - " + elementName, e);
201 | }
202 | }
203 |
204 | public void click(String element, MobileFindBy elementType, String elementName) {
205 | click(getMobileElement(element, elementType), elementName);
206 | }
207 |
208 | protected void enter(MobileElement element, String value, String elementName) {
209 | try {
210 | explicitlyWaitForElement(WaitStrategy.VISIBLE, element);
211 | doClear(element);
212 | element.sendKeys(value);
213 | ExtentReportLogger.logInfo("Entered value - " + value + " in the field " + elementName);
214 | } catch (Exception e) {
215 | ExtentReportLogger.logFail("Exception occurred while entering value in the field - " + elementName, e);
216 | }
217 | }
218 |
219 | protected void enterValueAndPressEnter(MobileElement element, String value, String elementName) {
220 | try {
221 | doClear(element);
222 | element.sendKeys(value, Keys.ENTER);
223 | ExtentReportLogger.logInfo("Entered value - " + value + " in the field " + elementName + " and pressed enter");
224 | } catch (Exception e) {
225 | ExtentReportLogger.logFail("Exception caught while entering value", e);
226 | }
227 | }
228 |
229 | protected void enter(String element, MobileFindBy elementType, String value, String elementName) {
230 | enter(getMobileElement(element, elementType), value, elementName);
231 | }
232 |
233 | public boolean isTextPresent(String containsText) {
234 | return DriverManager.getDriver().getPageSource().contains(containsText);
235 | }
236 |
237 | public void powerStateAndroid(String powerState) {
238 | switch (powerState) {
239 | case "ON":
240 | ((AndroidDriver) DriverManager.getDriver()).setPowerAC(PowerACState.ON);
241 | break;
242 | case "OFF":
243 | ((AndroidDriver) DriverManager.getDriver()).setPowerAC(PowerACState.OFF);
244 | break;
245 | default:
246 | ExtentReportLogger.warning("Voice state not available");
247 | break;
248 | }
249 | }
250 |
251 | /**
252 | * Swipe Down
253 | */
254 | public void swipeDown() {
255 | DriverManager.getDriver().executeScript("mobile:scroll",
256 | ImmutableMap.of("direction", "down"));
257 | ExtentReportLogger.logInfo("Swipe Down");
258 | }
259 |
260 | /**
261 | * Swipe Up
262 | */
263 | public void swipeUP() {
264 | DriverManager.getDriver().executeScript("mobile:scroll", ImmutableMap.of("direction", "up"));
265 | ExtentReportLogger.logInfo("Swipe Up");
266 | }
267 |
268 | /**
269 | * Accept Alert
270 | */
271 | public void acceptAlert() {
272 | DriverManager.getDriver().executeScript("mobile:acceptAlert");
273 | ExtentReportLogger.logInfo("Accept Alert");
274 | }
275 |
276 | /**
277 | * Dismiss Alert
278 | */
279 | public void dismissAlert() {
280 | DriverManager.getDriver().executeScript("mobile:dismissAlert");
281 | ExtentReportLogger.logInfo("Dismiss Alert");
282 | }
283 |
284 | /**
285 | * Long press key
286 | *
287 | * @param element element
288 | */
289 | public void longPress(MobileElement element) {
290 | try {
291 | new TouchAction<>(DriverManager.getDriver())
292 | .longPress(ElementOption.element(element))
293 | .perform();
294 | } catch (Exception e) {
295 | ExtentReportLogger.logFail("Exception caught while performing long press on the Mobile Element", e);
296 | }
297 | }
298 |
299 | /**
300 | * Scroll to specific location
301 | */
302 | public void scrollToLocation() {
303 | try {
304 | HashMap scrollElement = new HashMap<>();
305 | scrollElement.put("startX", 0.50);
306 | scrollElement.put("startY", 0.95);
307 | scrollElement.put("endX", 0.50);
308 | scrollElement.put("endY", 0.01);
309 | scrollElement.put("duration", 3.0);
310 | DriverManager.getDriver().executeScript("mobile: swipe", scrollElement);
311 | } catch (Exception e) {
312 | ExtentReportLogger.logFail("Exception caught when scrolling to specific location", e);
313 | }
314 | }
315 |
316 | public boolean checkListIsSorted(List listToSort) {
317 | if (!listToSort.isEmpty()) {
318 | try {
319 | if (Ordering.natural().isOrdered(listToSort)) {
320 | ExtentReportLogger.logPass("List is sorted");
321 | return true;
322 | } else {
323 | ExtentReportLogger.logInfo("List is not sorted");
324 | return false;
325 | }
326 | } catch (Exception e) {
327 | ExtentReportLogger.logFail("Exception caught when checking if list is sorted", e);
328 | }
329 | } else {
330 | ExtentReportLogger.warning("List is empty");
331 | }
332 | return false;
333 | }
334 |
335 | /**
336 | * Touch Actions
337 | *
338 | * @param a1 axis 1
339 | * @param b1 axis 2
340 | * @param a2 axis 3
341 | * @param b2 axis 4
342 | * @param time time
343 | */
344 | @SuppressWarnings("rawtypes")
345 | private void touchActions(int a1, int b1, int a2, int b2, int time) {
346 | TouchAction touchAction = new TouchAction(DriverManager.getDriver());
347 | touchAction.press(PointOption.point(a1, b1)).
348 | waitAction(WaitOptions.waitOptions(Duration.ofMillis(time))).
349 | moveTo(PointOption.point(a2, b2)).release();
350 | touchAction.perform();
351 | }
352 |
353 | /**
354 | * Swipe with axix
355 | *
356 | * @param x x axis
357 | * @param y y axis
358 | * @param x1 x1 axis
359 | * @param y1 y1 axis
360 | * @param time timeInMilli
361 | */
362 | protected void swipeAxis(int x, int y, int x1, int y1, int count, int time) {
363 | for (int i = 0; i < count; i++) {
364 | touchActions(x, y, x1, y1, time);
365 | }
366 | }
367 |
368 | /**
369 | * tap to element for 250sec
370 | *
371 | * @param androidElement element
372 | */
373 | @SuppressWarnings("rawtypes")
374 | public void tapByElement(MobileElement androidElement) {
375 | new TouchAction(DriverManager.getDriver())
376 | .tap(TapOptions.tapOptions().withElement(ElementOption.element(androidElement)))
377 | .waitAction(WaitOptions.waitOptions(Duration.ofMillis(250))).perform();
378 | }
379 |
380 | /**
381 | * Tap by coordinates
382 | *
383 | * @param x x
384 | * @param y y
385 | */
386 | @SuppressWarnings("rawtypes")
387 | public void tapByCoordinates(int x, int y) {
388 | new TouchAction(DriverManager.getDriver())
389 | .tap(PointOption.point(x, y))
390 | .waitAction(WaitOptions.waitOptions(Duration.ofMillis(250))).perform();
391 | }
392 |
393 | /**
394 | * Press by element
395 | *
396 | * @param element element
397 | * @param seconds time
398 | */
399 | @SuppressWarnings("rawtypes")
400 | public void pressByElement(MobileElement element, long seconds) {
401 | new TouchAction(DriverManager.getDriver())
402 | .press(ElementOption.element(element))
403 | .waitAction(WaitOptions.waitOptions(Duration.ofSeconds(seconds)))
404 | .release()
405 | .perform();
406 | }
407 |
408 | /**
409 | * LongPress by element
410 | *
411 | * @param element element
412 | * @param seconds time
413 | */
414 | @SuppressWarnings("rawtypes")
415 | public void longPressByElement(MobileElement element, long seconds) {
416 | new TouchAction(DriverManager.getDriver())
417 | .longPress(ElementOption.element(element))
418 | .waitAction(WaitOptions.waitOptions(Duration.ofSeconds(seconds)))
419 | .release()
420 | .perform();
421 | }
422 |
423 | /**
424 | * Press by co-ordinates
425 | *
426 | * @param x x
427 | * @param y y
428 | * @param seconds time
429 | */
430 | @SuppressWarnings("rawtypes")
431 | public void pressByCoordinates(int x, int y, long seconds) {
432 | new TouchAction(DriverManager.getDriver())
433 | .press(PointOption.point(x, y))
434 | .waitAction(WaitOptions.waitOptions(Duration.ofSeconds(seconds)))
435 | .release()
436 | .perform();
437 | }
438 |
439 | /**
440 | * Horizontal swipe by percentage
441 | *
442 | * @param startPercentage start
443 | * @param endPercentage end
444 | * @param anchorPercentage anchor
445 | */
446 | @SuppressWarnings("rawtypes")
447 | public void horizontalSwipeByPercentage(double startPercentage, double endPercentage, double anchorPercentage) {
448 | Dimension size = DriverManager.getDriver().manage().window().getSize();
449 | int anchor = (int) (size.height * anchorPercentage);
450 | int startPoint = (int) (size.width * startPercentage);
451 | int endPoint = (int) (size.width * endPercentage);
452 | new TouchAction(DriverManager.getDriver())
453 | .press(PointOption.point(startPoint, anchor))
454 | .waitAction(WaitOptions.waitOptions(Duration.ofMillis(1000)))
455 | .moveTo(PointOption.point(endPoint, anchor))
456 | .release().perform();
457 | }
458 |
459 | /**
460 | * Vertical swipe by percentage
461 | *
462 | * @param startPercentage start
463 | * @param endPercentage end
464 | * @param anchorPercentage anchor
465 | */
466 | @SuppressWarnings("rawtypes")
467 | public void verticalSwipeByPercentages(double startPercentage, double endPercentage, double anchorPercentage) {
468 | Dimension size = DriverManager.getDriver().manage().window().getSize();
469 | int anchor = (int) (size.width * anchorPercentage);
470 | int startPoint = (int) (size.height * startPercentage);
471 | int endPoint = (int) (size.height * endPercentage);
472 |
473 | new TouchAction(DriverManager.getDriver())
474 | .press(PointOption.point(anchor, startPoint))
475 | .waitAction(WaitOptions.waitOptions(Duration.ofMillis(1000)))
476 | .moveTo(PointOption.point(anchor, endPoint))
477 | .release().perform();
478 | }
479 |
480 | /**
481 | * Swipe by elements
482 | *
483 | * @param startElement start
484 | * @param endElement end
485 | */
486 | @SuppressWarnings("rawtypes")
487 | public void swipeByElements(MobileElement startElement, MobileElement endElement) {
488 | int startX = startElement.getLocation().getX() + (startElement.getSize().getWidth() / 2);
489 | int startY = startElement.getLocation().getY() + (startElement.getSize().getHeight() / 2);
490 |
491 | int endX = endElement.getLocation().getX() + (endElement.getSize().getWidth() / 2);
492 | int endY = endElement.getLocation().getY() + (endElement.getSize().getHeight() / 2);
493 |
494 | new TouchAction(DriverManager.getDriver())
495 | .press(PointOption.point(startX, startY))
496 | .waitAction(WaitOptions.waitOptions(Duration.ofMillis(1000)))
497 | .moveTo(PointOption.point(endX, endY))
498 | .release().perform();
499 | }
500 |
501 | /**
502 | * Multi touch by element
503 | *
504 | * @param androidElement element
505 | */
506 | @SuppressWarnings("rawtypes")
507 | public void multiTouchByElement(MobileElement androidElement) {
508 | TouchAction press = new TouchAction(DriverManager.getDriver())
509 | .press(ElementOption.element(androidElement))
510 | .waitAction(WaitOptions.waitOptions(Duration.ofSeconds(1)))
511 | .release();
512 |
513 | new MultiTouchAction(DriverManager.getDriver())
514 | .add(press)
515 | .perform();
516 | }
517 |
518 | /**
519 | * Swipe touch (UP,DOWN,LEFT,RIGHT)
520 | *
521 | * @param direction direction
522 | * @param count count
523 | */
524 | protected void swipe(String direction, int count, int time) {
525 | Dimension size = DriverManager.getDriver().manage().window().getSize();
526 | try {
527 | switch (direction) {
528 | case "left":
529 | case "LEFT":
530 | for (int i = 0; i < count; i++) {
531 | int startx = (int) (size.width * 0.8);
532 | int endx = (int) (size.width * 0.20);
533 | int starty = size.height / 2;
534 | touchActions(startx, starty, endx, starty, time);
535 | }
536 | break;
537 | case "right":
538 | case "RIGHT":
539 | for (int j = 0; j < count; j++) {
540 | int endx = (int) (size.width * 0.8);
541 | int startx = (int) (size.width * 0.20);
542 | int starty = size.height / 2;
543 | touchActions(startx, starty, endx, starty, time);
544 | }
545 | break;
546 | case "up":
547 | case "UP":
548 | for (int j = 0; j < count; j++) {
549 | int starty = (int) (size.height * 0.80);
550 | int endy = (int) (size.height * 0.20);
551 | int startx = size.width / 2;
552 | touchActions(startx, starty, startx, endy, time);
553 | }
554 | break;
555 | case "down":
556 | case "DOWN":
557 | for (int j = 0; j < count; j++) {
558 | int starty = (int) (size.height * 0.80);
559 | int endy = (int) (size.height * 0.20);
560 | int startx = size.width / 2;
561 | touchActions(startx, endy, startx, starty, time);
562 | }
563 | break;
564 | default:
565 | ExtentReportLogger.logInfo("Direction not found");
566 | break;
567 | }
568 | } catch (Exception e) {
569 | ExtentReportLogger.logFail("Exception caught while performing Swipe", e);
570 | }
571 | }
572 |
573 | protected void closeApp() {
574 | DriverManager.getDriver().closeApp();
575 | }
576 |
577 | protected void launchApp() {
578 | DriverManager.getDriver().launchApp();
579 | }
580 | }
581 |
--------------------------------------------------------------------------------
/src/main/java/com/automate/reports/ExtentReportLogger.java:
--------------------------------------------------------------------------------
1 | package com.automate.reports;
2 |
3 | import com.automate.enums.ConfigProperties;
4 | import com.automate.utils.configloader.PropertyUtils;
5 | import com.automate.utils.screenshot.ScreenshotService;
6 | import com.aventstack.extentreports.MediaEntityBuilder;
7 | import com.aventstack.extentreports.Status;
8 | import com.aventstack.extentreports.markuputils.ExtentColor;
9 | import com.aventstack.extentreports.markuputils.MarkupHelper;
10 | import lombok.AccessLevel;
11 | import lombok.NoArgsConstructor;
12 |
13 | @NoArgsConstructor(access = AccessLevel.PRIVATE)
14 | public final class ExtentReportLogger {
15 |
16 | public static void logPass(String message) {
17 | if (PropertyUtils.getPropertyValue(ConfigProperties.PASSED_STEP_SCREENSHOTS).equalsIgnoreCase("yes")) {
18 | ExtentReportManager.getExtentTest().pass(message,
19 | MediaEntityBuilder.createScreenCaptureFromBase64String(
20 | ScreenshotService.getScreenshotAsBase64()).build());
21 | } else {
22 | ExtentReportManager.getExtentTest().pass(MarkupHelper.createLabel(message, ExtentColor.GREEN));
23 | }
24 | }
25 |
26 | public static void logFail(String message, Throwable t) {
27 | if (PropertyUtils.getPropertyValue(ConfigProperties.FAILED_STEP_SCREENSHOTS).equalsIgnoreCase("yes")) {
28 | ExtentReportManager.getExtentTest().fail(MarkupHelper.createLabel(message, ExtentColor.RED))
29 | .fail(MediaEntityBuilder.createScreenCaptureFromBase64String(ScreenshotService.getScreenshotAsBase64()).build())
30 | .fail(t);
31 | } else {
32 | ExtentReportManager.getExtentTest().fail(message).fail(t);
33 | }
34 | }
35 |
36 | public static void logSkip(String message) {
37 | if (PropertyUtils.getPropertyValue(ConfigProperties.SKIPPED_STEP_SCREENSHOTS).equalsIgnoreCase("yes")) {
38 | ExtentReportManager.getExtentTest().skip(message,
39 | MediaEntityBuilder.createScreenCaptureFromBase64String(
40 | ScreenshotService.getScreenshotAsBase64()).build());
41 | } else {
42 | ExtentReportManager.getExtentTest().log(Status.SKIP, message);
43 | }
44 | }
45 |
46 | public static void logInfo(String message) {
47 | ExtentReportManager.getExtentTest().log(Status.INFO, message);
48 | }
49 |
50 | public static void warning(String message) {
51 | ExtentReportManager.getExtentTest().log(Status.WARNING, message);
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/main/java/com/automate/reports/ExtentReportManager.java:
--------------------------------------------------------------------------------
1 | package com.automate.reports;
2 |
3 | import com.automate.constants.FrameworkConstants;
4 | import com.automate.driver.manager.DeviceManager;
5 | import com.automate.driver.manager.PlatformManager;
6 | import com.automate.enums.CategoryType;
7 | import com.aventstack.extentreports.ExtentReports;
8 | import com.aventstack.extentreports.ExtentTest;
9 | import com.aventstack.extentreports.reporter.ExtentSparkReporter;
10 | import com.aventstack.extentreports.reporter.configuration.Theme;
11 | import lombok.AccessLevel;
12 | import lombok.NoArgsConstructor;
13 |
14 | import java.awt.Desktop;
15 | import java.io.File;
16 | import java.io.IOException;
17 | import java.net.InetAddress;
18 | import java.util.Objects;
19 |
20 | @NoArgsConstructor(access = AccessLevel.PRIVATE)
21 | public final class ExtentReportManager {
22 |
23 | private static final ExtentSparkReporter extentSparkReporter = new ExtentSparkReporter(FrameworkConstants.getExtentReportPath());
24 | private static final ThreadLocal threadLocalExtentTest = new ThreadLocal<>();
25 | private static ExtentReports extentReports;
26 | private static InetAddress ip;
27 | private static String hostname;
28 |
29 | /**
30 | * This method is to initialize the Extent Report
31 | */
32 | public static void initExtentReport() {
33 | try {
34 | if (Objects.isNull(extentReports)) {
35 | extentReports = new ExtentReports();
36 | extentReports.attachReporter(extentSparkReporter);
37 | ip = InetAddress.getLocalHost();
38 | hostname = ip.getHostName();
39 | extentReports.setSystemInfo("Host Name", hostname);
40 | extentReports.setSystemInfo("Environment", "Mobile Automation - Appium");
41 | extentReports.setSystemInfo("User Name", System.getProperty("user.name"));
42 | extentSparkReporter.config().setDocumentTitle("HTML Report");
43 | extentSparkReporter.config().setReportName("Mobile Automation Test");
44 | extentSparkReporter.config().setTheme(Theme.DARK);
45 | }
46 | } catch (Exception e) {
47 | e.printStackTrace();
48 | }
49 | }
50 |
51 | public static void createTest(String testCaseName) {
52 | setExtentTest(extentReports.createTest(testCaseName));
53 | }
54 |
55 | public static void flushExtentReport() {
56 | if (Objects.nonNull(extentReports)) {
57 | extentReports.flush();
58 | }
59 | unload();
60 | try {
61 | Desktop.getDesktop().browse(new File(FrameworkConstants.getExtentReportPath()).toURI());
62 | } catch (IOException e) {
63 | e.printStackTrace();
64 | }
65 | }
66 |
67 | public static ExtentTest getExtentTest() {
68 | return threadLocalExtentTest.get();
69 | }
70 |
71 | static void setExtentTest(ExtentTest test) {
72 | threadLocalExtentTest.set(test);
73 | }
74 |
75 | static void unload() {
76 | threadLocalExtentTest.remove();
77 | }
78 |
79 | public static void addAuthors(String[] authors) {
80 | for (String author : authors) {
81 | getExtentTest().assignAuthor(author);
82 | }
83 | }
84 |
85 | public static void addCategories(CategoryType[] categories) {
86 | for (CategoryType category : categories) {
87 | getExtentTest().assignCategory(category.toString());
88 | }
89 | }
90 |
91 | public static void addDevices() {
92 | getExtentTest().assignDevice(PlatformManager.getPlatformName() + "-" + DeviceManager.getDeviceName());
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/src/main/java/com/automate/utils/AppiumServerManager.java:
--------------------------------------------------------------------------------
1 | package com.automate.utils;
2 |
3 | import com.automate.constants.FrameworkConstants;
4 | import com.automate.enums.ConfigProperties;
5 | import com.automate.utils.configloader.PropertyUtils;
6 | import io.appium.java_client.service.local.AppiumDriverLocalService;
7 | import io.appium.java_client.service.local.AppiumServiceBuilder;
8 | import io.appium.java_client.service.local.flags.GeneralServerFlag;
9 | import lombok.AccessLevel;
10 | import lombok.NoArgsConstructor;
11 |
12 | import java.io.File;
13 | import java.io.IOException;
14 | import java.net.ServerSocket;
15 |
16 | @NoArgsConstructor(access = AccessLevel.PRIVATE)
17 | public final class AppiumServerManager {
18 |
19 | private static AppiumDriverLocalService service;
20 |
21 | static boolean checkIfServerIsRunning(int port) {
22 | boolean isServerRunning = false;
23 | try {
24 | ServerSocket serverSocket = new ServerSocket(port);
25 | serverSocket.close();
26 | } catch (IOException e) {
27 | isServerRunning = true;
28 | }
29 | return isServerRunning;
30 | }
31 |
32 | public static void startAppiumServer() {
33 | if (PropertyUtils.getPropertyValue(ConfigProperties.START_APPIUM_SERVER).equalsIgnoreCase("yes")) {
34 | if (!AppiumServerManager.checkIfServerIsRunning(FrameworkConstants.APPIUM_SERVER_PORT)) {
35 | //Build the Appium service
36 | AppiumServiceBuilder builder = new AppiumServiceBuilder();
37 | builder.usingDriverExecutable(new File(FrameworkConstants.NODEJS_PATH))
38 | .withAppiumJS(new File(FrameworkConstants.APPIUM_JS_PATH))
39 | .withIPAddress(FrameworkConstants.APPIUM_SERVER_HOST)
40 | .usingPort(FrameworkConstants.APPIUM_SERVER_PORT)
41 | .withArgument(GeneralServerFlag.SESSION_OVERRIDE)
42 | .withArgument(GeneralServerFlag.ALLOW_INSECURE, "chromedriver_autodownload")
43 | .withLogFile(new File(FrameworkConstants.getAppiumServerLogsPath()));
44 | //Start the server with the builder
45 | service = AppiumDriverLocalService.buildService(builder);
46 | // service = AppiumDriverLocalService.buildDefaultService();
47 | service.start();
48 | service.clearOutPutStreams();
49 | }
50 | }
51 | }
52 |
53 | public static void stopAppiumServer() {
54 | if (PropertyUtils.getPropertyValue(ConfigProperties.START_APPIUM_SERVER).equalsIgnoreCase("yes")) {
55 | service.stop();
56 | Runtime runtime = Runtime.getRuntime();
57 | try {
58 | runtime.exec("taskkill /F /IM node.exe");
59 | runtime.exec("taskkill /F /IM cmd.exe");
60 | } catch (Exception e) {
61 | e.printStackTrace();
62 | }
63 | }
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/src/main/java/com/automate/utils/DecodeUtils.java:
--------------------------------------------------------------------------------
1 | package com.automate.utils;
2 |
3 | import lombok.AccessLevel;
4 | import lombok.NoArgsConstructor;
5 |
6 | import java.util.Base64;
7 |
8 | @NoArgsConstructor(access = AccessLevel.PRIVATE)
9 | public final class DecodeUtils {
10 |
11 | public static String getDecodedString(String encodedString) {
12 | return new String(Base64.getDecoder().decode(encodedString.getBytes()));
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/main/java/com/automate/utils/TestUtils.java:
--------------------------------------------------------------------------------
1 | package com.automate.utils;
2 |
3 | import lombok.AccessLevel;
4 | import lombok.NoArgsConstructor;
5 | import org.apache.logging.log4j.LogManager;
6 | import org.apache.logging.log4j.Logger;
7 | import org.w3c.dom.Document;
8 | import org.w3c.dom.Element;
9 | import org.w3c.dom.Node;
10 | import org.w3c.dom.NodeList;
11 | import org.xml.sax.SAXException;
12 |
13 | import javax.xml.parsers.DocumentBuilder;
14 | import javax.xml.parsers.DocumentBuilderFactory;
15 | import javax.xml.parsers.ParserConfigurationException;
16 | import java.io.File;
17 | import java.io.IOException;
18 | import java.io.InputStream;
19 | import java.util.Arrays;
20 | import java.util.HashMap;
21 | import java.util.Objects;
22 |
23 | @NoArgsConstructor(access = AccessLevel.PRIVATE)
24 | public final class TestUtils {
25 |
26 | public static void deleteFolder(File file) {
27 | File[] files = file.listFiles();
28 | if (Objects.nonNull(files))
29 | Arrays.asList(files).forEach(content -> file.delete());
30 | }
31 |
32 | public static HashMap parseStringXML(InputStream in) throws IOException, SAXException, ParserConfigurationException {
33 | HashMap map = new HashMap<>();
34 |
35 | DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
36 | DocumentBuilder builder = docFactory.newDocumentBuilder();
37 |
38 | Document document = builder.parse(in);
39 | document.getDocumentElement().normalize();
40 |
41 | Element root = document.getDocumentElement();
42 | NodeList nList = document.getElementsByTagName("string");
43 |
44 | for (int temp = 0; temp < nList.getLength(); temp++) {
45 | Node node = nList.item(temp);
46 | if (node.getNodeType() == Node.ELEMENT_NODE) {
47 | Element element = (Element) node;
48 | map.put(element.getAttribute("name"), element.getTextContent());
49 | }
50 | }
51 | return map;
52 | }
53 |
54 | public static Logger log() {
55 | return LogManager.getLogger(Thread.currentThread().getStackTrace()[2].getClassName());
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/main/java/com/automate/utils/configloader/JsonUtils.java:
--------------------------------------------------------------------------------
1 | package com.automate.utils.configloader;
2 |
3 | import com.automate.constants.FrameworkConstants;
4 | import com.automate.customexceptions.InvalidPathException;
5 | import com.automate.customexceptions.JsonFileUsageException;
6 | import com.automate.enums.ConfigJson;
7 | import com.fasterxml.jackson.core.type.TypeReference;
8 | import com.fasterxml.jackson.databind.ObjectMapper;
9 | import com.jayway.jsonpath.JsonPath;
10 | import lombok.AccessLevel;
11 | import lombok.NoArgsConstructor;
12 |
13 | import java.io.File;
14 | import java.io.IOException;
15 | import java.util.HashMap;
16 | import java.util.Map;
17 | import java.util.Objects;
18 |
19 | @NoArgsConstructor(access = AccessLevel.PRIVATE)
20 | public final class JsonUtils {
21 |
22 | private static Map map;
23 |
24 | public static String getValue(String key) {
25 | try {
26 | return JsonPath.read(new File(FrameworkConstants.CONFIG_JSON_PATH), key);
27 | } catch (IOException e) {
28 | throw new InvalidPathException("Check the config.json");
29 | }
30 | }
31 |
32 | static void readJson(String jsonPath) {
33 | try {
34 | map = new ObjectMapper().readValue(new File(jsonPath),
35 | new TypeReference>() {
36 | });
37 | } catch (IOException e) {
38 | throw new JsonFileUsageException("IOException occurred while reading Json file in the specified path");
39 | }
40 | }
41 |
42 | public static String getConfig(ConfigJson key) {
43 | readJson(FrameworkConstants.CONFIG_JSON_PATH);
44 | if (Objects.isNull(map.get(key.name().toLowerCase()))) {
45 | throw new JsonFileUsageException("Property name - " + key + " is not found. Please check the config.json");
46 | }
47 | return map.get(key.name().toLowerCase());
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/main/java/com/automate/utils/configloader/PropertyUtils.java:
--------------------------------------------------------------------------------
1 | package com.automate.utils.configloader;
2 |
3 | import com.automate.constants.FrameworkConstants;
4 | import com.automate.customexceptions.PropertyFileUsageException;
5 | import com.automate.enums.ConfigProperties;
6 | import lombok.AccessLevel;
7 | import lombok.NoArgsConstructor;
8 |
9 | import java.io.FileInputStream;
10 | import java.io.IOException;
11 | import java.util.Objects;
12 | import java.util.Properties;
13 |
14 | @NoArgsConstructor(access = AccessLevel.PRIVATE)
15 | public final class PropertyUtils {
16 |
17 | private static final Properties property = new Properties();
18 |
19 | static void loadProperties(String propertyFilePath) {
20 | try (FileInputStream input = new FileInputStream(propertyFilePath)) {
21 | property.load(input);
22 | } catch (IOException e) {
23 | throw new PropertyFileUsageException("IOException occurred while loading Property file in the specified path");
24 | }
25 | }
26 |
27 | public static String getPropertyValue(ConfigProperties key) {
28 | loadProperties(FrameworkConstants.CONFIG_PROPERTIES_PATH);
29 | if (Objects.isNull(property.getProperty(key.name().toLowerCase())) || Objects.isNull(key.name().toLowerCase())) {
30 | throw new PropertyFileUsageException("Property name - " + key + " is not found. Please check the config.properties");
31 | }
32 | return property.getProperty(key.name().toLowerCase());
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/main/java/com/automate/utils/dataprovider/DataProviderUtils.java:
--------------------------------------------------------------------------------
1 | package com.automate.utils.dataprovider;
2 |
3 | import com.automate.constants.FrameworkConstants;
4 | import com.automate.entity.LoginData;
5 | import com.automate.entity.SearchData;
6 | import com.automate.entity.TestData;
7 | import lombok.AccessLevel;
8 | import lombok.NoArgsConstructor;
9 | import org.testng.annotations.DataProvider;
10 |
11 | import java.lang.reflect.Method;
12 | import java.util.ArrayList;
13 | import java.util.List;
14 | import java.util.Map;
15 | import java.util.function.Predicate;
16 |
17 | @NoArgsConstructor(access = AccessLevel.PRIVATE)
18 | public final class DataProviderUtils {
19 |
20 | private static List