├── .gitignore
├── pom.xml
└── src
├── main
└── java
│ └── com
│ ├── config
│ ├── ConfigFactory.java
│ └── FrameworkConfig.java
│ ├── entity
│ ├── EmployeeDetails.java
│ └── LoginDetails.java
│ ├── factory
│ └── BrowserContextFactory.java
│ ├── manager
│ ├── BrowserManager.java
│ ├── NavigationManager.java
│ └── PlaywrightManager.java
│ └── pages
│ ├── AddEmployeePage.java
│ ├── HomePage.java
│ └── LoginPage.java
└── test
├── java
└── com
│ └── tests
│ ├── EmployeeInformationPageTest.java
│ ├── ReuseLoginStateTest.java
│ ├── base
│ ├── ReusableLoginTestSetup.java
│ └── TestSetup.java
│ └── testdata
│ ├── EmployeeTestData.java
│ └── LoginTestData.java
└── resources
├── config.properties
├── images
└── browserstack.jpeg
├── junit-platform.properties
└── staging-config.properties
/.gitignore:
--------------------------------------------------------------------------------
1 | # Project exclude paths
2 | /target/
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | org.example
8 | Playwright
9 | 1.0-SNAPSHOT
10 |
11 |
12 | 11
13 | 11
14 | UTF-8
15 |
16 |
17 |
18 |
19 | com.microsoft.playwright
20 | playwright
21 | 1.25.0
22 |
23 |
24 |
25 | org.junit.jupiter
26 | junit-jupiter
27 | 5.8.2
28 | test
29 |
30 |
31 |
32 | org.aeonbits.owner
33 | owner
34 | 1.0.12
35 |
36 |
37 |
38 | uk.co.jemos.podam
39 | podam
40 | 7.2.11.RELEASE
41 |
42 |
43 |
44 | org.projectlombok
45 | lombok
46 | 1.18.24
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/src/main/java/com/config/ConfigFactory.java:
--------------------------------------------------------------------------------
1 | package com.config;
2 |
3 | import org.aeonbits.owner.ConfigCache;
4 |
5 | public final class ConfigFactory {
6 |
7 | private ConfigFactory() {
8 | }
9 |
10 | public static FrameworkConfig getConfig() {
11 | return ConfigCache.getOrCreate(FrameworkConfig.class);
12 | }
13 |
14 | }
--------------------------------------------------------------------------------
/src/main/java/com/config/FrameworkConfig.java:
--------------------------------------------------------------------------------
1 | package com.config;
2 |
3 | import org.aeonbits.owner.Config;
4 |
5 | @Config.LoadPolicy(Config.LoadType.MERGE)
6 | @Config.Sources({
7 | "system:properties",
8 | "system:env",
9 | "file:${user.dir}/src/test/resources/config.properties",
10 | "file:${user.dir}/src/test/resources/staging-config.properties",
11 | "file:${user.dir}/src/test/resources/development-config.properties",
12 | })
13 | public interface FrameworkConfig extends Config {
14 |
15 | @DefaultValue("chrome")
16 | String browser();
17 |
18 | boolean headless();
19 |
20 | @Key("${environment}.url")
21 | String url();
22 |
23 | @DefaultValue("staging")
24 | String environment();
25 | }
--------------------------------------------------------------------------------
/src/main/java/com/entity/EmployeeDetails.java:
--------------------------------------------------------------------------------
1 | package com.entity;
2 |
3 | import lombok.Data;
4 | import uk.co.jemos.podam.common.PodamStringValue;
5 |
6 | @Data
7 | public class EmployeeDetails {
8 | private String firstName;
9 | private String lastName;
10 | private String middleName;
11 | private int employeeId;
12 |
13 | @PodamStringValue(strValue = "/src/test/resources/images/browserstack.jpeg")
14 | private String profilePicturePath;
15 | }
--------------------------------------------------------------------------------
/src/main/java/com/entity/LoginDetails.java:
--------------------------------------------------------------------------------
1 | package com.entity;
2 |
3 | import lombok.Data;
4 | import uk.co.jemos.podam.common.PodamStringValue;
5 |
6 | @Data
7 | public class LoginDetails {
8 |
9 | @PodamStringValue(strValue = "Admin")
10 | private String userName;
11 |
12 | @PodamStringValue(strValue = "admin123")
13 | private String password;
14 | }
--------------------------------------------------------------------------------
/src/main/java/com/factory/BrowserContextFactory.java:
--------------------------------------------------------------------------------
1 | package com.factory;
2 |
3 | import com.config.ConfigFactory;
4 | import com.config.FrameworkConfig;
5 | import com.microsoft.playwright.Browser;
6 | import com.microsoft.playwright.BrowserContext;
7 | import com.microsoft.playwright.BrowserType;
8 | import com.microsoft.playwright.Playwright;
9 |
10 | import java.nio.file.Paths;
11 | import java.util.HashMap;
12 | import java.util.Map;
13 | import java.util.function.Function;
14 | import java.util.function.Supplier;
15 |
16 | public final class BrowserContextFactory {
17 |
18 | private BrowserContextFactory() {
19 | }
20 |
21 | private static final Map>> BROWSER_MAP = new HashMap<>();
22 | private static final FrameworkConfig CONFIG = ConfigFactory.getConfig();
23 | private static final String BROWSER = CONFIG.browser().toLowerCase();
24 | private static final boolean IS_HEADLESS = CONFIG.headless();
25 |
26 | static {
27 | BROWSER_MAP.put("firefox", playwright -> () -> playwright.firefox().launch(getLaunchOptions()));
28 | BROWSER_MAP.put("chrome", playwright -> () -> playwright.chromium().launch(getLaunchOptions()));
29 | }
30 |
31 | public static BrowserContext getBrowserContext(Playwright playwright) {
32 | return BROWSER_MAP.get(BROWSER).apply(playwright).get().newContext();
33 | }
34 |
35 | public static BrowserContext getExistingBrowserContext(Playwright playwright) {
36 | return BROWSER_MAP.get(BROWSER).apply(playwright)
37 | .get()
38 | .newContext(new Browser.NewContextOptions().setStorageStatePath(Paths.get("reusable-login-state.json")));
39 | }
40 |
41 | private static BrowserType.LaunchOptions getLaunchOptions() {
42 | return new BrowserType.LaunchOptions().setHeadless(IS_HEADLESS).setChannel(BROWSER);
43 | }
44 |
45 | }
--------------------------------------------------------------------------------
/src/main/java/com/manager/BrowserManager.java:
--------------------------------------------------------------------------------
1 | package com.manager;
2 |
3 | import com.factory.BrowserContextFactory;
4 | import com.microsoft.playwright.BrowserContext;
5 | import com.microsoft.playwright.Playwright;
6 |
7 | public final class BrowserManager {
8 |
9 | private BrowserManager() {
10 | }
11 |
12 | public static void initBrowser() {
13 | Playwright playwright = PlaywrightManager.getPlaywrightInstance();
14 | BrowserContext browserContext = BrowserContextFactory.getBrowserContext(playwright);
15 | PlaywrightManager.setBrowserContext(browserContext);
16 | }
17 |
18 | public static void initBrowserWithExistingLoginState() {
19 | Playwright playwright = PlaywrightManager.getPlaywrightInstance();
20 | BrowserContext browserContext = BrowserContextFactory.getExistingBrowserContext(playwright);
21 | PlaywrightManager.setBrowserContext(browserContext);
22 | }
23 |
24 | public static void tearDown() {
25 | PlaywrightManager.cleanUp();
26 | }
27 | }
--------------------------------------------------------------------------------
/src/main/java/com/manager/NavigationManager.java:
--------------------------------------------------------------------------------
1 | package com.manager;
2 |
3 | import com.config.ConfigFactory;
4 |
5 | public final class NavigationManager {
6 |
7 | private NavigationManager() {
8 | }
9 |
10 | private static final String URL = ConfigFactory.getConfig().url();
11 |
12 | public static void launchUrl() {
13 | PlaywrightManager.getPage().navigate(URL);
14 | }
15 | }
--------------------------------------------------------------------------------
/src/main/java/com/manager/PlaywrightManager.java:
--------------------------------------------------------------------------------
1 | package com.manager;
2 |
3 | import com.microsoft.playwright.BrowserContext;
4 | import com.microsoft.playwright.Page;
5 | import com.microsoft.playwright.Playwright;
6 |
7 | import static java.util.Objects.isNull;
8 |
9 | public final class PlaywrightManager {
10 |
11 | private PlaywrightManager() {
12 | }
13 |
14 | private static final ThreadLocal PLAYWRIGHT_THREAD_LOCAL = ThreadLocal.withInitial(Playwright::create);
15 | private static final ThreadLocal BROWSER_CONTEXT_THREAD_LOCAL = new ThreadLocal<>();
16 | private static final ThreadLocal PAGE_THREAD_LOCAL = new ThreadLocal<>();
17 |
18 | static Playwright getPlaywrightInstance() {
19 | return PLAYWRIGHT_THREAD_LOCAL.get();
20 | }
21 |
22 | public static BrowserContext getBrowserContext() {
23 | return BROWSER_CONTEXT_THREAD_LOCAL.get();
24 | }
25 |
26 | static void setBrowserContext(BrowserContext browserContext) {
27 | BROWSER_CONTEXT_THREAD_LOCAL.set(browserContext);
28 | }
29 |
30 | public static Page getPage() {
31 | if (isNull(PAGE_THREAD_LOCAL.get())) {
32 | setPage();
33 | }
34 | return PAGE_THREAD_LOCAL.get();
35 | }
36 |
37 | static void setPage() {
38 | PAGE_THREAD_LOCAL.set(getBrowserContext().newPage());
39 | }
40 |
41 | public static void cleanUp() {
42 | BROWSER_CONTEXT_THREAD_LOCAL.get().close();
43 | PLAYWRIGHT_THREAD_LOCAL.get().close();
44 | BROWSER_CONTEXT_THREAD_LOCAL.remove();
45 | PAGE_THREAD_LOCAL.remove();
46 | PLAYWRIGHT_THREAD_LOCAL.remove();
47 | }
48 | }
--------------------------------------------------------------------------------
/src/main/java/com/pages/AddEmployeePage.java:
--------------------------------------------------------------------------------
1 | package com.pages;
2 |
3 | import com.entity.EmployeeDetails;
4 | import com.microsoft.playwright.Locator;
5 | import com.microsoft.playwright.Page;
6 | import com.microsoft.playwright.assertions.LocatorAssertions;
7 |
8 | import java.nio.file.Paths;
9 |
10 | import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat;
11 |
12 | public class AddEmployeePage {
13 |
14 | private final Page page;
15 |
16 | public AddEmployeePage(Page page) {
17 | this.page = page;
18 | }
19 |
20 | private static final String FIRST_NAME = "input[name='firstName']";
21 | private static final String MIDDLE_NAME = "input[name='middleName']";
22 | private static final String LAST_NAME = "input[name='lastName']";
23 | private static final String UPLOAD_FILE = "input[type='file']";
24 | private static final String EMPLOYEE_ID = "//label[text()='Employee Id']/following::input";
25 | private static final String SAVE_BUTTON = "//button[@type='submit']";
26 | private static final String SUCCESS_MESSAGE = "text='Success'";
27 |
28 | public AddEmployeePage addNewEmployee(EmployeeDetails employeeDetails) {
29 | page.locator(FIRST_NAME).fill(employeeDetails.getFirstName(), new Locator.FillOptions().setForce(true));
30 | page.locator(LAST_NAME).fill(employeeDetails.getLastName());
31 | page.locator(MIDDLE_NAME).fill(employeeDetails.getMiddleName());
32 | page.fill(EMPLOYEE_ID, String.valueOf(employeeDetails.getEmployeeId()));
33 | page.setInputFiles(UPLOAD_FILE, Paths.get(System.getProperty("user.dir") + employeeDetails.getProfilePicturePath()));
34 | page.locator(SAVE_BUTTON).click();
35 | return this;
36 | }
37 |
38 | public void verifyWhetherEmployeeCreatedSuccessfully() {
39 | assertThat(page.locator(SUCCESS_MESSAGE)).isVisible(new LocatorAssertions.IsVisibleOptions().setTimeout(10_000));
40 | }
41 | }
--------------------------------------------------------------------------------
/src/main/java/com/pages/HomePage.java:
--------------------------------------------------------------------------------
1 | package com.pages;
2 |
3 | import com.microsoft.playwright.Locator;
4 | import com.microsoft.playwright.Page;
5 |
6 | import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat;
7 |
8 | public class HomePage {
9 |
10 | private final Page page;
11 |
12 | private static final String PIM = "text=PIM";
13 | private static final String EMPLOYEE_INFORMATION = "//h5";
14 | private static final String ADD_EMPLOYEE_BUTTON = "text='Add Employee'";
15 |
16 | public HomePage(Page page) {
17 | this.page = page;
18 | }
19 |
20 | public HomePage clickPIMMenu() {
21 | page.locator(PIM)
22 | .first()
23 | .click();
24 | return this;
25 | }
26 |
27 | public AddEmployeePage navigateToAddEmployeePage() {
28 | page.locator(ADD_EMPLOYEE_BUTTON).first().click(new Locator.ClickOptions().setForce(true));
29 | return new AddEmployeePage(page);
30 | }
31 |
32 | public void verifyEmployeeInformationTextIsDisplayed() {
33 | assertThat(page.locator(EMPLOYEE_INFORMATION))
34 | .containsText("Employee Information");
35 | }
36 | }
--------------------------------------------------------------------------------
/src/main/java/com/pages/LoginPage.java:
--------------------------------------------------------------------------------
1 | package com.pages;
2 |
3 | import com.entity.LoginDetails;
4 | import com.manager.PlaywrightManager;
5 | import com.microsoft.playwright.Page;
6 |
7 | public class LoginPage {
8 |
9 | private final Page page;
10 |
11 | private static final String USERNAME = "input[name='username']";
12 | private static final String PASSWORD = "input[name='password']";
13 | private static final String LOGIN_BUTTON = "button[type='submit']";
14 |
15 | private LoginPage(Page page) {
16 | this.page = page;
17 | }
18 |
19 | public static LoginPage getInstance() {
20 | return new LoginPage(PlaywrightManager.getPage());
21 | }
22 |
23 | public HomePage loginToApplication(LoginDetails loginDetails) {
24 | page.locator(USERNAME).fill(loginDetails.getUserName());
25 | page.locator(PASSWORD).fill(loginDetails.getPassword());
26 | page.locator(LOGIN_BUTTON).click();
27 | return new HomePage(page);
28 | }
29 | }
--------------------------------------------------------------------------------
/src/test/java/com/tests/EmployeeInformationPageTest.java:
--------------------------------------------------------------------------------
1 | package com.tests;
2 |
3 | import com.entity.EmployeeDetails;
4 | import com.entity.LoginDetails;
5 | import com.pages.LoginPage;
6 | import com.tests.base.TestSetup;
7 | import com.tests.testdata.EmployeeTestData;
8 | import com.tests.testdata.LoginTestData;
9 | import org.junit.jupiter.api.Test;
10 | import org.junit.jupiter.api.parallel.Execution;
11 | import org.junit.jupiter.api.parallel.ExecutionMode;
12 |
13 | @Execution(value = ExecutionMode.CONCURRENT)
14 | class EmployeeInformationPageTest extends TestSetup {
15 |
16 | private final EmployeeDetails employeeDetails = EmployeeTestData.getRandomEmployeeDetails();
17 | private final LoginDetails loginDetails = LoginTestData.getValidLoginDetails();
18 |
19 | @Test
20 | void testWhetherEmployeeInformationIsDisplayed() {
21 | LoginPage.getInstance()
22 | .loginToApplication(loginDetails)
23 | .clickPIMMenu()
24 | .verifyEmployeeInformationTextIsDisplayed();
25 | }
26 |
27 | @Test
28 | void testNewEmployeeCanBeCreated() {
29 | LoginPage.getInstance()
30 | .loginToApplication(loginDetails)
31 | .clickPIMMenu()
32 | .navigateToAddEmployeePage()
33 | .addNewEmployee(employeeDetails)
34 | .verifyWhetherEmployeeCreatedSuccessfully();
35 | }
36 | }
--------------------------------------------------------------------------------
/src/test/java/com/tests/ReuseLoginStateTest.java:
--------------------------------------------------------------------------------
1 | package com.tests;
2 |
3 | import com.manager.PlaywrightManager;
4 | import com.pages.HomePage;
5 | import com.tests.base.ReusableLoginTestSetup;
6 | import org.junit.jupiter.api.Test;
7 |
8 | class ReuseLoginStateTest extends ReusableLoginTestSetup {
9 |
10 | @Test
11 | void testReusableBrowserContext() {
12 | new HomePage(PlaywrightManager.getPage())
13 | .clickPIMMenu()
14 | .verifyEmployeeInformationTextIsDisplayed();
15 | }
16 | }
--------------------------------------------------------------------------------
/src/test/java/com/tests/base/ReusableLoginTestSetup.java:
--------------------------------------------------------------------------------
1 | package com.tests.base;
2 |
3 | import com.manager.BrowserManager;
4 | import com.manager.NavigationManager;
5 | import org.junit.jupiter.api.AfterEach;
6 | import org.junit.jupiter.api.BeforeEach;
7 |
8 | public class ReusableLoginTestSetup {
9 |
10 | @BeforeEach
11 | public void setUp() {
12 | BrowserManager.initBrowserWithExistingLoginState();
13 | NavigationManager.launchUrl();
14 | }
15 |
16 | @AfterEach
17 | public void tearDown() {
18 | BrowserManager.tearDown();
19 | }
20 | }
--------------------------------------------------------------------------------
/src/test/java/com/tests/base/TestSetup.java:
--------------------------------------------------------------------------------
1 | package com.tests.base;
2 |
3 | import com.manager.BrowserManager;
4 | import com.manager.NavigationManager;
5 | import org.junit.jupiter.api.AfterEach;
6 | import org.junit.jupiter.api.BeforeEach;
7 |
8 | public class TestSetup {
9 |
10 | @BeforeEach
11 | public void setUp() {
12 | BrowserManager.initBrowser();
13 | NavigationManager.launchUrl();
14 | }
15 |
16 | @AfterEach
17 | public void tearDown() {
18 | BrowserManager.tearDown();
19 | }
20 | }
--------------------------------------------------------------------------------
/src/test/java/com/tests/testdata/EmployeeTestData.java:
--------------------------------------------------------------------------------
1 | package com.tests.testdata;
2 |
3 | import com.entity.EmployeeDetails;
4 | import uk.co.jemos.podam.api.PodamFactory;
5 | import uk.co.jemos.podam.api.PodamFactoryImpl;
6 |
7 | public final class EmployeeTestData {
8 |
9 | private static final PodamFactory FACTORY = new PodamFactoryImpl();
10 |
11 | private EmployeeTestData() {
12 | }
13 |
14 | public static EmployeeDetails getRandomEmployeeDetails() {
15 | return FACTORY.manufacturePojo(EmployeeDetails.class);
16 | }
17 | }
--------------------------------------------------------------------------------
/src/test/java/com/tests/testdata/LoginTestData.java:
--------------------------------------------------------------------------------
1 | package com.tests.testdata;
2 |
3 | import com.entity.LoginDetails;
4 | import uk.co.jemos.podam.api.PodamFactory;
5 | import uk.co.jemos.podam.api.PodamFactoryImpl;
6 |
7 | public final class LoginTestData {
8 |
9 | private static final PodamFactory FACTORY = new PodamFactoryImpl();
10 |
11 | private LoginTestData() {
12 | }
13 |
14 | public static LoginDetails getValidLoginDetails() {
15 | return FACTORY.manufacturePojo(LoginDetails.class);
16 | }
17 | }
--------------------------------------------------------------------------------
/src/test/resources/config.properties:
--------------------------------------------------------------------------------
1 | browser=chrome
2 | headless=false
3 |
--------------------------------------------------------------------------------
/src/test/resources/images/browserstack.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/amuthansakthivel/OrangeHrmPlaywrightFramework/ea5d723e6d2363fdd2d48c06c89bfd910bfef813/src/test/resources/images/browserstack.jpeg
--------------------------------------------------------------------------------
/src/test/resources/junit-platform.properties:
--------------------------------------------------------------------------------
1 | junit.jupiter.execution.parallel.enabled=true
--------------------------------------------------------------------------------
/src/test/resources/staging-config.properties:
--------------------------------------------------------------------------------
1 | staging.url=https://opensource-demo.orangehrmlive.com/web/index.php/auth/login
--------------------------------------------------------------------------------