소스 정보
- 저장소
- thiagofernandes1987-create/APEX
- 최근 소스 활동
- 2026년 7월 21일 11:53
- 감지된 SKILL.md 언어
- 영어
- 스타
- 2
- 포크
- 0
설치 방법
기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.
소스 파일 검토
설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.
메뉴
기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.
설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
직접 명령은 검토 Prompt를 거치지 않습니다. 실행하기 전에 소스를 확인하세요.
npx skills add https://github.com/thiagofernandes1987-create/APEX --skill playwright-java명령은 한 줄로 유지됩니다. 복사하기 전에 가로로 스크롤해 전체 내용을 확인하세요.
로컬 사본을 원하시나요? SkillsMP에서 현재 제공할 수 있는 파일을 다운로드하세요.
Token-aware reasoning workflow with real tools: picks an operating mode to control cost, runs a structured pipeline (decompose → validate → verify → snapshot), and gives Claude Program-of-Thought, RK4/Euler, a code gate, and a safe skill router. Use when: multi-step or high-stakes tasks, real math, precise computation, audits, or the user mentions APEX, PoT, pipeline, or scientific mode.
**v00.33.0**: Ingested from antigravity-awesome-skills community repo
run multiple local CLI agents in parallel (separate tmux sessions)
SOC 직업 분류 기준
SKILL.md 표시 중
| name | playwright-java |
| description | "Use when scaffolding a new Playwright Java project from scratch" |
This skill produces production-quality, enterprise-grade Playwright Java test code. It enforces the Page Object Model (POM), strict locator strategies, thread-safe parallel execution, and full Allure reporting integration. Targets Java 17+ and Playwright 1.44+.
Supporting reference files are available for deeper topics:
| Topic | File |
|---|---|
| Maven POM, ConfigReader, Docker/CI setup | references/config.md |
| Component pattern, dropdowns, uploads, waits | references/page-objects.md |
| Full assertion API, soft assertions, visual testing | references/assertions.md |
| Fixtures, test data factory, auth state, retry | references/fixtures.md |
| Drop-in base class templates | templates/BaseTest.java, templates/BasePage.java |
Thread.sleep() with proper waitsUse this matrix to pick the right pattern before writing any code:
| User Request | Approach |
|---|---|
| New project from scratch | Full scaffold — see references/config.md |
| Single feature test | POM page class + JUnit5 test class |
| API + UI hybrid | APIRequestContext alongside Page |
| Cross-browser | @MethodSource parameterized over browser names |
| Flaky test fix | Replace sleep with waitFor / waitForResponse |
| CI integration | playwright install --with-deps in pipeline |
| Parallel execution | junit-platform.properties + ThreadLocal |
| Rich reporting | Allure + Playwright trace + video recording |
Always use this layout when creating a new project:
src/
├── test/
│ ├── java/com/company/tests/
│ │ ├── base/
│ │ │ ├── BaseTest.java ← templates/BaseTest.java
│ │ │ └── BasePage.java ← templates/BasePage.java
│ │ ├── pages/
│ │ │ └── LoginPage.java
│ │ ├── tests/
│ │ │ └── LoginTest.java
│ │ ├── utils/
│ │ │ ├── TestDataFactory.java
│ │ │ └── WaitUtils.java
│ │ └── config/
│ │ └── ConfigReader.java
│ └── resources/
│ ├── test.properties
│ ├── junit-platform.properties
│ └── testdata/users.json
pom.xml
public class BaseTest {
protected static ThreadLocal<Playwright> playwrightTL = new ThreadLocal<>();
protected static ThreadLocal<Browser> browserTL = new ThreadLocal<>();
protected static ThreadLocal<BrowserContext> contextTL = new ThreadLocal<>();
protected static ThreadLocal<Page> pageTL = new ThreadLocal<>();
protected Page page() { return pageTL.get(); }
@BeforeEach
void setUp() {
Playwright playwright = Playwright.create();
playwrightTL.set(playwright);
Browser browser = resolveBrowser(playwright).launch(
new BrowserType.LaunchOptions()
.setHeadless(ConfigReader.isHeadless()));
browserTL.set(browser);
BrowserContext context = browser.newContext(new Browser.NewContextOptions()
.setViewportSize(1920, 1080)
.setRecordVideoDir(Paths.get("target/videos/"))
.setLocale("en-US"));
context.tracing().start(new .StartOptions()
.setScreenshots().setSnapshots());
contextTL.set(context);
pageTL.set(context.newPage());
}
{
testInfo.getDisplayName().replaceAll(, );
contextTL.get().tracing().stop( .StopOptions()
.setPath(Paths.get( + name + )));
pageTL.get().close();
contextTL.get().close();
browserTL.get().close();
playwrightTL.get().close();
}
BrowserType {
(System.getProperty(, ).toLowerCase()) {
-> pw.firefox();
-> pw.webkit();
-> pw.chromium();
};
}
}
public class LoginPage extends BasePage {
// Declare ALL locators as fields — never inline in action methods
private final Locator emailInput;
private final Locator passwordInput;
private final Locator loginButton;
private final Locator errorMessage;
public LoginPage(Page page) {
super(page);
emailInput = page.getByLabel("Email address");
passwordInput = page.getByLabel("Password");
loginButton = page.getByRole(AriaRole.BUTTON,
new Page.GetByRoleOptions().setName("Sign in"));
errorMessage = page.getByTestId("login-error");
}
@Override protected String getUrl() { return "/login"; }
// Navigation methods return the next Page Object — enables fluent chaining
public DashboardPage loginAs(String email, String password) {
fill(emailInput, email);
fill(passwordInput, password);
clickAndWaitForNav(loginButton);
return new DashboardPage(page);
}
public LoginPage loginExpectingError(String email, String password) {
fill(emailInput, email);
fill(passwordInput, password);
loginButton.click();
errorMessage.waitFor();
;
}
String { errorMessage.textContent(); }
}
@ExtendWith(AllureJunit5.class)
class LoginTest extends BaseTest {
private LoginPage loginPage;
@BeforeEach
void openLoginPage() {
loginPage = new LoginPage(page());
loginPage.navigate();
}
@Test
@Severity(SeverityLevel.BLOCKER)
@DisplayName("Valid credentials redirect to dashboard")
void shouldLoginWithValidCredentials() {
User user = TestDataFactory.getDefaultUser();
DashboardPage dash = loginPage.loginAs(user.email(), user.password());
assertThat(page()).hasURL(Pattern.compile(".*/dashboard"));
assertThat(dash.getWelcomeBanner()).containsText("Welcome, " + user.firstName());
}
@Test
void shouldShowErrorOnInvalidCredentials() {
loginPage.loginExpectingError("bad@test.com", "wrongpass");
SoftAssertions softly = new SoftAssertions();
softly.assertThat(loginPage.getErrorMessage()).contains("Invalid email or password");
softly.assertThat(page()).hasURL(Pattern.compile(".*/login"));
softly.assertAll();
}
@ParameterizedTest
@MethodSource("provideInvalidCredentials")
void {
loginPage.loginExpectingError(email, password);
assertThat(loginPage.getErrorMessage()).containsText(expectedError);
}
Stream<Arguments> {
Stream.of(
Arguments.of(, , ),
Arguments.of(, , ),
Arguments.of(, , )
);
}
}
@Test
void shouldDisplayNewlyCreatedOrder() {
// Arrange via API — faster than navigating through UI
APIRequestContext api = page().context().request();
APIResponse response = api.post("/api/orders",
RequestOptions.create()
.setHeader("Authorization", "Bearer " + authToken)
.setData(Map.of("productId", "SKU-001", "quantity", 2)));
assertThat(response).isOK();
String orderId = new JsonParser().parse(response.text())
.getAsJsonObject().get("id").getAsString();
OrdersPage orders = new OrdersPage(page());
orders.navigate();
assertThat(orders.getOrderRowById(orderId)).isVisible();
}
@Test
void shouldHandleApiFailureGracefully() {
page().route("**/api/products", route -> route.fulfill(
new Route.FulfillOptions()
.setStatus(503)
.setBody("{\"error\":\"Service Unavailable\"}")
.setContentType("application/json")));
ProductsPage products = new ProductsPage(page());
products.navigate();
assertThat(products.getErrorBanner())
.hasText("We're having trouble loading products. Please try again.");
}
@ParameterizedTest
@MethodSource("browsers")
void shouldRenderCheckoutOnAllBrowsers(String browserName) {
System.setProperty("browser", browserName);
new CheckoutPage(page()).navigate();
assertThat(page().locator(".checkout-form")).isVisible();
}
static Stream<String> browsers() {
return Stream.of("chromium", "firefox", "webkit");
}
# src/test/resources/junit-platform.properties
junit.jupiter.execution.parallel.enabled=true
junit.jupiter.execution.parallel.mode.default=concurrent
junit.jupiter.execution.parallel.config.strategy=fixed
junit.jupiter.execution.parallel.config.fixed.parallelism=4
- name: Install Playwright browsers
run: mvn exec:java -e -Dexec.mainClass=com.microsoft.playwright.CLI -Dexec.args="install --with-deps"
- name: Run tests
run: mvn test -Dbrowser=${{ matrix.browser }} -Dheadless=true
- name: Upload traces on failure
uses: actions/upload-artifact@v4
if: failure()
with:
name: playwright-traces
path: target/traces/
- name: Upload Allure results
uses: actions/upload-artifact@v4
if: always()
with:
name: allure-results
path: target/allure-results/
ThreadLocal<Page> for every parallel-safe test suiteLocator fields at the top of the Page Object classassertThat(locator) — it auto-retries until timeoutgetByRole, getByLabel, getByTestId as first-choice locators@BeforeEach and stop with a file path in @AfterEachSoftAssertions when validating multiple fields on a single pagestorageState) to skip login across test classesThread.sleep() — replace with waitFor() or waitForResponse()ConfigReader.getBaseUrl()Playwright instance inside a Page ObjectProblem: Tests fail randomly in parallel mode
Solution: Ensure every test creates its own Playwright → Browser → BrowserContext → Page chain via ThreadLocal. Never share a Page across threads.
Problem: assertThat(locator).isVisible() times out even when the element appears
Solution: Increase timeout with .setTimeout(10_000) or raise context.setDefaultTimeout() in BaseTest.
Problem: Thread.sleep(2000) was added but tests are still flaky
Solution: Replace with page.waitForResponse("**/api/endpoint", () -> action()) or assertThat(locator).hasText("Done") which polls automatically.
Problem: Playwright trace zip is empty or missing
Solution: Ensure tracing().start() is called before test actions and tracing().stop() is in @AfterEach — not @AfterAll.
Problem: Allure report is blank or missing steps
Solution: Add the AspectJ agent to maven-surefire-plugin <argLine> in pom.xml — see references/config.md for the exact snippet.
Problem: storageState auth file is stale and tests redirect to login
Solution: Re-run AuthSetup to regenerate target/auth/user-state.json before the suite, or add a @BeforeAll that conditionally refreshes it.
@rest-assured-java — Use for pure API test suites without any UI interaction@selenium-java — Legacy alternative; prefer Playwright for all new projects@allure-reporting — Deep-dive into Allure annotations, categories, and history trends@testcontainers-java — Use alongside this skill when tests need a live database or service@github-actions-ci — For building complete multi-browser matrix CI pipelinesUse —