Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

1.0.1 #2182

Merged
merged 25 commits into from
May 16, 2023
Merged

1.0.1 #2182

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
a10fd55
temp commit
akamarouski Apr 14, 2023
eb48b11
feature(MethodOwner): add opportunity to set MethodOwner annotation o…
akamarouski Apr 18, 2023
158b2ea
change version of carina-webdriver to 1.0.3.P1-SNAPSHOT
akamarouski Apr 18, 2023
e5a35a1
Merge pull request #2181 from zebrunner/#2175
akamarouski Apr 18, 2023
6df021d
temp
akamarouski Apr 20, 2023
6051700
doc(README.md): add table with carina projects
akamarouski Apr 20, 2023
434b5ac
Merge pull request #2185 from zebrunner/#2184
vdelendik Apr 20, 2023
025e575
refactor: get agent and testng transitively from carina-webdriver module
akamarouski Apr 21, 2023
3333ccc
Merge pull request #2186 from zebrunner/#2174
akamarouski Apr 21, 2023
4086d0b
bump up carina-webdriver to 1.0.3.P3-SNAPSHOT
akamarouski Apr 27, 2023
f31bfa0
#2188: improved owner detection
vdelendik Apr 27, 2023
420ac21
Merge pull request #2189 from zebrunner/#2188
akamarouski Apr 27, 2023
482b783
change version of carina-webdriver to 1.0.3.P4-SNAPSHOT
akamarouski May 3, 2023
d8f2b4b
change version of carina-webdriver to 1.0.3.P5-SNAPSHOT
akamarouski May 4, 2023
9b01eaa
change version of carina-webdriver to 1.0.3.P6-SNAPSHOT
akamarouski May 4, 2023
e7186a1
bump up carina-utils to 1.0.4.P1-SNAPSHOT
akamarouski May 5, 2023
761f536
doc(configuration.md): document test_naming_pattern
akamarouski May 10, 2023
9897571
doc(migration.md): remove ':' from doc header
akamarouski May 10, 2023
6e956ef
doc(migration.md): add code snippet for getting key from file
akamarouski May 11, 2023
6ba34e9
doc(configuration.md): refactor formatting of list
akamarouski May 11, 2023
b41bdd0
Merge pull request #2197 from zebrunner/#2194
vdelendik May 11, 2023
32f0b4b
change version of carina-webdriver to 1.0.3.P7-SNAPSHOT
akamarouski May 11, 2023
1300b1b
change version of carina-webdriver to 1.0.3.P8-SNAPSHOT
akamarouski May 15, 2023
9f4e83b
fix(MethodOwner.java): add opportunity to chain MethodOwner for class
akamarouski May 16, 2023
4f18909
bump up carina-webdriver to 1.0.3
akamarouski May 16, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,55 @@ Carina is a Java-based test automation framework that unites all testing layers:

![Alt text](https://github.com/zebrunner/carina/raw/master/docs/img/carina_overview.png "Carina Overview")


The Carina Framework ecosystem consists of the following modules:
<table>
<tr>
<th>Project Name</th>
<th>Description</th>
</tr>
<tr>
<td>[Carina API](https://github.com/zebrunner/carina-api)</td>
<td>For API testing, based on RestAssured library. <b>Optional</b></td>
</tr>
<tr>
<td>[Carina AWS S3](https://github.com/zebrunner/carina-aws-s3)</td>
<td>A set of utilities for working with Amazon S3. <b>Optional</b></td>
</tr>
<tr>
<td>[Carina Azure](https://github.com/zebrunner/carina-azure)</td>
<td>A set of utilities for working with Azure. <b>Optional</b></td>
</tr>
<tr>
<td>[Carina AppCenter](https://github.com/zebrunner/carina-appcenter)</td>
<td>A set of utilities for working with AppCenter. <b>Optional</b></td>
</tr>
<tr>
<td>[Carina DataProvider](https://github.com/zebrunner/carina-dataprovider)</td>
<td>Provides the ability to use xls/csv as data sources. <b>Optional</b></td>
</tr>
<tr>
<td>[Carina WebDriver](https://github.com/zebrunner/carina-webdriver)</td>
<td>Contains logic for creating sessions. <b>Part of Carina Core</b></td>
</tr>
<tr>
<td>[Carina Utils](https://github.com/zebrunner/carina-utils)</td>
<td>Provides a set of tools for all components of Carina Framework. <b>Part of Carina Core</b></td>
</tr>
<tr>
<td>[Carina Commons](https://github.com/zebrunner/carina-commons)</td>
<td>Contains interfaces for pluggable dependencies (e.g. carina-azure). <b>Part of Carina Core</b></td>
</tr>
<tr>
<td>[Carina Proxy](https://github.com/zebrunner/carina-proxy)</td>
<td>Contains utilities for working with proxies. <b>Part of Carina Core</b></td>
</tr>
<tr>
<td>[Carina Crypto](https://github.com/zebrunner/carina-crypto)</td>
<td>Contains utilities for encryption/decryption. <b>Part of Carina Core</b></td>
</tr>
</table>

## Sponsor
<p align="center">
<a href="https://zebrunner.com/"><img alt="Zebrunner" src="https://github.com/zebrunner/zebrunner/raw/master/docs/img/zebrunner_intro.png"></a>
Expand Down
19 changes: 19 additions & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,11 @@ All the project configuration properties are located in a **_config.properties**
<td>Executing rules logic: test_run_rules={RULE_NAME_ENUM}=>{RULE_VALUE1}&&{RULE_VALUE2};;...</td>
<td>test_run_rules=PRIORITY=>P1&amp;&amp;P2&&P4;;OWNER=>owner;;TAGS=>tag1=temp||!!feature=reg</td>
</tr>
<tr>
<td>test_naming_pattern</td>
<td>The pattern by which the name of the test method will be formed.</td>
<td>{tuid} {test_name} - {method_name}</td>
</tr>
<tr>
<td>retry_interval</td>
<td>Timeout interval in **ms** between calling HTML DOM for the element. **Default: 100**. For mobile automation specify in between 500-1000</td>
Expand Down Expand Up @@ -396,6 +401,20 @@ test_run_rules=PRIORITY=>!!P1;;OWNER=>Josh&&!!Jake;;TAGS=>feature=web&&!!type=sm
#and if they are not for smoke web or if they are for android.
```

### Changing the pattern of forming the name of the test method
The name of the test method is formed based on the pattern provided in the `test_naming_pattern` parameter.
The pattern can be formed from the following parts:
1) `{test_name}` - test name (content of `name` attribute of `<test>` tag in xml)
2) `{tuid}` - TUID, see [doc](https://zebrunner.github.io/carina/advanced/dataprovider/#adding-test-unique-identifier-tuid-to-the-test-name-using-java-data-provider).
3) `{method_name}` - test method name
4) `{method_priority}` - test method priority (number)
5) `{method_thread_pool_size}` - the number of threads to be used when invoking the method on parallel
6) `{group_names}` - the groups this method belongs to, possibly added to the groups declared on the class.
7) `{method_description}` - description of test method
8) `{test_class}` - simple name of test class this method belongs to

Default pattern: `test_naming_pattern={tuid} {test_name} - {method_name}`

###FAQ
**Where is a recommended place to declare configuration parameters?**

Expand Down
9 changes: 8 additions & 1 deletion docs/migration.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,13 @@ Also when adding carina-dataprovider dependency exclude testng dependency as the
* The `crypto_key_value` parameter is added to specify the crypto key (specified in [configuration](https://zebrunner.github.io/carina/configuration/)).
* The presence of a crypto key is optional, as long as there is no encrypted data.

Since a file was used as a key before, you can use the following code to get the key from it and writing it to the
`crypto_key_value` parameter (for example, in beforeSuite):

```
R.CONFIG.put("crypto_key_value", Files.readString(Path.of(R.CONFIG.get("crypto_key_path")), StandardCharsets.UTF_8));
```
However, this approach is not recommended. It is recommended to put `crypto_key_value` in environment variables.

For more info check [documentation](https://zebrunner.github.io/carina/advanced/security/).

Expand Down Expand Up @@ -91,7 +98,7 @@ However, there are methods that cannot be accessed using the interface. In such
`com.zebrunner.carina.webdriver.listener.DriverListener` class. However, if you are casting to AndroidDriver for example, you must
be sure it's AndroidDriver.

### Project side dependency requirements:
### Project side dependency requirements

* The `net.bytebuddy.byte-buddy` version should be `1.12.10`.
* The `com.zebrunner.agent-core` version should be `1.9.3`.
Expand Down
18 changes: 1 addition & 17 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -49,16 +49,13 @@
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>11</java.version>
<zebrunner-testng-pluggable.version>1.9.3</zebrunner-testng-pluggable.version>
<carina-webdriver.version>1.0.2</carina-webdriver.version>
<carina-webdriver.version>1.0.3</carina-webdriver.version>
<carina-utils.version>1.0.3</carina-utils.version>
<!-- Logging -->
<slf4j.version>1.7.30</slf4j.version>
<log4j.version>2.17.1</log4j.version>
<!-- Commons -->
<commons-lang3.version>3.5</commons-lang3.version>
<!-- Testings -->
<testng.version>7.6.1</testng.version>
<testng-foundation.version>2.0.1</testng-foundation.version>
<maven-source-plugin.version>3.0.1</maven-source-plugin.version>
<maven-compiler-plugin.version>3.8.0</maven-compiler-plugin.version>
Expand Down Expand Up @@ -107,11 +104,6 @@
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.zebrunner</groupId>
<artifactId>agent-testng-pluggable</artifactId>
<version>${zebrunner-testng-pluggable.version}</version>
</dependency>

<dependency>
<groupId>org.slf4j</groupId>
Expand Down Expand Up @@ -142,14 +134,6 @@
<version>${commons-lang3.version}</version>
</dependency>

<!-- Test utilities -->
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>${testng.version}</version>
<scope>compile</scope>
</dependency>

<dependency>
<groupId>com.nordstrom.tools</groupId>
<artifactId>testng-foundation</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -793,11 +793,15 @@ public void run() {
*/
private void registerOwner() {
String owner = System.getProperty("BUILD_USER_ID");
if (owner == null) {
if (owner == null || owner.isEmpty()) {
owner = System.getenv("BUILD_USER_ID");
}

if (owner == null || owner.isEmpty()) {
owner = System.getenv("USERNAME");
}
if (owner != null) {
System.out.println(owner);

if (owner != null && !owner.isEmpty()) {
Label.attachToTestRun("Owner", owner);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,20 +22,26 @@
import java.lang.annotation.Target;

/**
* This defines the 'MethodOwner' annotation used to specify the
* TestNG methods owners.
*
* Allows to specify the owner of the test method/test class.<br>
* The order of priorities when choosing the owner of the test method: <br>
* 1. The annotation on a test method containing a suitable plaftorm.<br>
* 2. The annotation on a test class containing a suitable platform.<br>
* 3. The annotation on a test method without the specified platform.<br>
* 4 The annotation on a test class withot the specified platform.<br>
* <br>
*
* The expected platform is determined based on the "capabilities.platformName" in the {@link com.zebrunner.carina.utils.R#CONFIG} (case-insensitive).
*/
@Repeatable(MethodOwner.List.class)
@Retention(value = RetentionPolicy.RUNTIME)
@Target(value = ElementType.METHOD)
@Target(value = { ElementType.METHOD, ElementType.TYPE })
public @interface MethodOwner {
String owner() default "";

String platform() default "";

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Target({ElementType.METHOD, ElementType.TYPE})
@interface List {
MethodOwner[] value();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,67 +15,111 @@
*******************************************************************************/
package com.zebrunner.carina.core.registrar.ownership;

import java.lang.invoke.MethodHandles;
import java.lang.reflect.Method;
import java.util.Optional;

import org.openqa.selenium.remote.CapabilityType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.zebrunner.carina.utils.Configuration;
import com.zebrunner.agent.core.registrar.maintainer.MaintainerResolver;
import org.apache.commons.lang3.StringUtils;
import com.zebrunner.carina.utils.R;

public class Ownership implements MaintainerResolver {
// todo think about adding validation for the platform duplicates in method/class annotations

private static final Logger LOGGER = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
// todo move to AbstractCapabilities
private static final String CAPABILITIES_CONFIG_PREFIX = "capabilities.";

@Override
public String resolve(Class<?> clazz, Method method) {
String owner = StringUtils.EMPTY;
// Get a handle to the class and method
// We can't use getMethod() because we may have parameterized tests
// for which we don't know the matching signature
String methodName = method.getName();
Method testMethod = null;
Method[] possibleMethods = clazz.getMethods();
for (Method possibleMethod : possibleMethods) {
if (possibleMethod.getName().equals(methodName)) {
testMethod = possibleMethod;
break;
}
}

// do a scan for single Methodowner annotation as well)
if (testMethod != null && testMethod.isAnnotationPresent(MethodOwner.class)) {
MethodOwner methodAnnotation = testMethod.getAnnotation(MethodOwner.class);
owner = methodAnnotation.owner();
// In order not to check for null further
if (method == null || clazz == null) {
return null;
}
// scan all MethodOwner annotations to find default ownership without any platform
if (testMethod != null && testMethod.isAnnotationPresent(MethodOwner.List.class)) {
MethodOwner.List methodAnnotation = testMethod.getAnnotation(MethodOwner.List.class);
for (MethodOwner methodOwner : methodAnnotation.value()) {
String actualPlatform = methodOwner.platform();
if (actualPlatform.isEmpty()) {
owner = methodOwner.owner();
break;
}

String expectedPlatform = R.CONFIG.get(CAPABILITIES_CONFIG_PREFIX + CapabilityType.PLATFORM_NAME);
Optional<MethodOwner> possibleMethodOwner = getMethodOwner(method, expectedPlatform);
Optional<MethodOwner> possibleClassOwner = getClassOwner(clazz, expectedPlatform);

// resolve platform-specific method owner
if (possibleMethodOwner.isPresent()) {
MethodOwner methodOwner = possibleMethodOwner.get();
if (!methodOwner.platform().isEmpty()) {
return methodOwner.owner();
}
}

//do one more scan using platform ownership filter if any to override default owner value
if (testMethod != null && testMethod.isAnnotationPresent(MethodOwner.List.class)) {
MethodOwner.List methodAnnotation = testMethod.getAnnotation(MethodOwner.List.class);
for (MethodOwner methodOwner : methodAnnotation.value()) {

String actualPlatform = methodOwner.platform();
String expectedPlatform = Configuration.getPlatform();

if (!actualPlatform.isEmpty() && isValidPlatform(actualPlatform, expectedPlatform)) {
owner = methodOwner.owner();
}
// resolve platform-specific class owner
if (possibleClassOwner.isPresent()) {
MethodOwner classOwner = possibleClassOwner.get();
if (!classOwner.platform().isEmpty()) {
return classOwner.owner();
}
}

return owner;
String suitableAnyPlatformOwner = null;
// resolve all-other-platforms method/class owner
if (possibleMethodOwner.isPresent()) {
suitableAnyPlatformOwner = possibleMethodOwner.get().owner();
} else if (possibleClassOwner.isPresent()) {
suitableAnyPlatformOwner = possibleClassOwner.get().owner();
}

return suitableAnyPlatformOwner;
}

private static boolean isValidPlatform(String actualPlatform, String expectedPlatform) {
return actualPlatform.equalsIgnoreCase(expectedPlatform);

/**
* Get suitable method owner
*
* @param method test method
* @param expectedPlatform expected platform
* @return {@link Optional} of {@link MethodOwner}
*/
private static Optional<MethodOwner> getMethodOwner(Method method, String expectedPlatform) {
Optional<MethodOwner> suitableMethodOwner = Optional.empty();
if (method.isAnnotationPresent(MethodOwner.class)) {
MethodOwner[] owners = new MethodOwner[1];
owners[0] = method.getAnnotation(MethodOwner.class);
suitableMethodOwner = getOwner(owners, expectedPlatform);
} else if (method.isAnnotationPresent(MethodOwner.List.class)) {
suitableMethodOwner = getOwner(method.getAnnotation(MethodOwner.List.class).value(), expectedPlatform);
}
return suitableMethodOwner;
}

/**
* Get suitable class owner
*
* @param clazz test class
* @param expectedPlatform expected platform
* @return {@link Optional} of {@link MethodOwner}
*/
private static Optional<MethodOwner> getClassOwner(Class<?> clazz, String expectedPlatform) {
Optional<MethodOwner> suitableMethodOwner = Optional.empty();
if (clazz.isAnnotationPresent(MethodOwner.class)) {
MethodOwner[] owners = new MethodOwner[1];
owners[0] = clazz.getAnnotation(MethodOwner.class);
suitableMethodOwner = getOwner(owners, expectedPlatform);
} else if (clazz.isAnnotationPresent(MethodOwner.List.class)) {
suitableMethodOwner = getOwner(clazz.getAnnotation(MethodOwner.List.class).value(), expectedPlatform);
}
return suitableMethodOwner;
}

}
private static Optional<MethodOwner> getOwner(MethodOwner[] owners, String expectedPlatform) {
MethodOwner suitableOwner = null;
for (MethodOwner owner : owners) {
if (owner.platform().isEmpty()) {
suitableOwner = owner;
} else if (owner.platform().equalsIgnoreCase(expectedPlatform)) {
suitableOwner = owner;
// If an annotation was found suitable for a specific platform, there is no point in continuing to search further.
break;
}
}
return Optional.ofNullable(suitableOwner);
}
}