Add CLAUDE.md and AGENTS.md for AI-assisted development guidance, analysis report with screenshots, and Playwright-based e2e test for signal cancellation flow. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
153 lines
4.7 KiB
TypeScript
153 lines
4.7 KiB
TypeScript
import { test, expect, Page } from "@playwright/test";
|
|
|
|
const TEST_USER = { username: "testuser", password: "testpass123" };
|
|
|
|
async function login(page: Page) {
|
|
await page.goto("/login");
|
|
await page.locator("#username").fill(TEST_USER.username);
|
|
await page.locator("#password").fill(TEST_USER.password);
|
|
await page.locator('button[type="submit"]').click();
|
|
await page.waitForURL("**/", { timeout: 10000 });
|
|
}
|
|
|
|
test.describe("Signal Cancel & Related Pages", () => {
|
|
test.beforeEach(async ({ page }) => {
|
|
await login(page);
|
|
});
|
|
|
|
test("should access signals page and see signal table", async ({ page }) => {
|
|
await page.goto("/signals");
|
|
|
|
// Wait for page title to appear
|
|
await expect(page.getByText("KJB 매매 신호")).toBeVisible({
|
|
timeout: 10000,
|
|
});
|
|
|
|
// Signal table should be visible
|
|
await expect(page.locator("table").first()).toBeVisible();
|
|
|
|
// Summary cards should be visible
|
|
await expect(page.getByText("매수 신호")).toBeVisible();
|
|
await expect(page.getByText("매도 신호", { exact: true })).toBeVisible();
|
|
|
|
await page.screenshot({
|
|
path: "../docs/screenshots/signals-page.png",
|
|
fullPage: true,
|
|
});
|
|
});
|
|
|
|
test("should show cancel button for EXECUTED signals", async ({ page }) => {
|
|
await page.goto("/signals");
|
|
|
|
await expect(page.getByText("KJB 매매 신호")).toBeVisible({
|
|
timeout: 10000,
|
|
});
|
|
|
|
// Switch to history view for executed signals
|
|
const historyButton = page.getByText("신호 이력");
|
|
if (await historyButton.isVisible()) {
|
|
await historyButton.click();
|
|
await page.waitForTimeout(2000);
|
|
}
|
|
|
|
// Check if any executed signal rows exist
|
|
const executedBadges = page.locator('text="실행됨"');
|
|
const count = await executedBadges.count();
|
|
|
|
if (count > 0) {
|
|
// There should be a cancel button near the executed signal
|
|
const cancelButtons = page.locator('button:has-text("취소")');
|
|
const cancelCount = await cancelButtons.count();
|
|
expect(cancelCount).toBeGreaterThan(0);
|
|
|
|
await page.screenshot({
|
|
path: "../docs/screenshots/signals-executed-with-cancel.png",
|
|
fullPage: true,
|
|
});
|
|
} else {
|
|
// No executed signals - verify the page structure is correct
|
|
console.log(
|
|
"No EXECUTED signals found - cancel button test skipped (no data)"
|
|
);
|
|
await expect(page.locator("table").first()).toBeVisible();
|
|
|
|
await page.screenshot({
|
|
path: "../docs/screenshots/signals-history.png",
|
|
fullPage: true,
|
|
});
|
|
}
|
|
});
|
|
|
|
test("should show realized/unrealized PnL cards on portfolio detail page", async ({
|
|
page,
|
|
}) => {
|
|
// First check if any portfolio exists
|
|
await page.goto("/portfolio");
|
|
await page.waitForTimeout(2000);
|
|
|
|
// Try to find a portfolio link
|
|
const portfolioLinks = page.locator('a[href^="/portfolio/"]');
|
|
const linkCount = await portfolioLinks.count();
|
|
|
|
if (linkCount > 0) {
|
|
await portfolioLinks.first().click();
|
|
await page.waitForTimeout(3000);
|
|
} else {
|
|
await page.goto("/portfolio/1");
|
|
await page.waitForTimeout(3000);
|
|
}
|
|
|
|
// Check for realized/unrealized PnL cards
|
|
const realizedPnlLabel = page.getByText("실현 수익");
|
|
const unrealizedPnlLabel = page.getByText("미실현 수익");
|
|
|
|
const hasRealizedCard = await realizedPnlLabel
|
|
.isVisible()
|
|
.catch(() => false);
|
|
const hasUnrealizedCard = await unrealizedPnlLabel
|
|
.isVisible()
|
|
.catch(() => false);
|
|
|
|
if (hasRealizedCard && hasUnrealizedCard) {
|
|
await expect(realizedPnlLabel).toBeVisible();
|
|
await expect(unrealizedPnlLabel).toBeVisible();
|
|
await expect(page.getByText("매도 확정 손익")).toBeVisible();
|
|
await expect(page.getByText("보유 중 평가 손익")).toBeVisible();
|
|
} else {
|
|
console.log(
|
|
"Portfolio detail page may not have data - PnL cards not visible"
|
|
);
|
|
}
|
|
|
|
await page.screenshot({
|
|
path: "../docs/screenshots/portfolio-detail.png",
|
|
fullPage: true,
|
|
});
|
|
});
|
|
|
|
test("should render strategy compare page", async ({ page }) => {
|
|
await page.goto("/strategy/compare");
|
|
|
|
// Wait for page title
|
|
await expect(
|
|
page.getByRole("heading", { name: "전략 비교" })
|
|
).toBeVisible({
|
|
timeout: 10000,
|
|
});
|
|
|
|
// Check description text
|
|
await expect(
|
|
page.getByText("멀티팩터, 퀄리티, 밸류모멘텀")
|
|
).toBeVisible();
|
|
|
|
// Check the compare execution button
|
|
const runButton = page.getByText("전략 비교 실행");
|
|
await expect(runButton).toBeVisible();
|
|
|
|
await page.screenshot({
|
|
path: "../docs/screenshots/strategy-compare.png",
|
|
fullPage: true,
|
|
});
|
|
});
|
|
});
|