├── .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 --------------------------------------------------------------------------------