Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 61083a5bb1 | |||
| 66fb86d23c | |||
| 16f7c6097c | |||
| 7232635ed0 | |||
| b42b98d560 | |||
| f216660afa | |||
| b31b43e30e | |||
| 86bd9ef8ff | |||
| 2fd9984a45 | |||
| 91330ec94c | |||
| 08102c8684 |
@@ -9,3 +9,4 @@ Authentication__Naver__ClientId=
|
|||||||
Authentication__Naver__ClientSecret=
|
Authentication__Naver__ClientSecret=
|
||||||
Authentication__Kakao__ClientId=
|
Authentication__Kakao__ClientId=
|
||||||
Authentication__Kakao__ClientSecret=
|
Authentication__Kakao__ClientSecret=
|
||||||
|
# CI deploy trigger requires a real push on master.
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
name: TaxBaik CI/CD
|
name: TaxBaik CI/CD
|
||||||
|
|
||||||
on:
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
|
|||||||
@@ -1931,6 +1931,48 @@ else
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
### CI Deploy 트러블슈팅 하네스 (2026-06-28)
|
||||||
|
|
||||||
|
커밋 후 배포가 동작하지 않는다고 판단하기 전에 아래 순서로 확인한다. 추측으로 runner, secret, 커밋 제목을 원인으로 단정하지 않는다.
|
||||||
|
|
||||||
|
1. **푸시 결과 확인**
|
||||||
|
```powershell
|
||||||
|
git push origin master 2>&1 | Select-String "master|To|Processed|remote"
|
||||||
|
```
|
||||||
|
`master -> master`가 보이면 Git push는 성공이다. 이 단계는 CI 실행 성공을 의미하지 않는다.
|
||||||
|
|
||||||
|
2. **Actions run 생성 확인**
|
||||||
|
```powershell
|
||||||
|
$headers = @{ Authorization = "token $env:GITEA_TOKEN_TAXBAIK" }
|
||||||
|
$runs = Invoke-RestMethod -Headers $headers -Uri "http://178.104.200.7/api/v1/repos/kjh2064/taxbaik/actions/runs?limit=10"
|
||||||
|
$runs.workflow_runs | Select-Object id,path,event,head_sha,display_title,status,conclusion
|
||||||
|
```
|
||||||
|
`deploy.yml@refs/heads/master`, `event=push`, 최신 `head_sha`가 있어야 배포가 실제로 시작된 것이다.
|
||||||
|
|
||||||
|
3. **workflow 파싱 검증**
|
||||||
|
```powershell
|
||||||
|
curl.exe -sS -w "`nHTTP_STATUS:%{http_code}`n" `
|
||||||
|
-H "Authorization: token $env:GITEA_TOKEN_TAXBAIK" `
|
||||||
|
-H "Content-Type: application/json" `
|
||||||
|
-X POST "http://178.104.200.7/api/v1/repos/kjh2064/taxbaik/actions/workflows/deploy.yml/dispatches?return_run_details=true" `
|
||||||
|
--data '{"ref":"refs/heads/master","inputs":{}}'
|
||||||
|
```
|
||||||
|
`failed to unmarshal workflow content`가 나오면 `.gitea/workflows/deploy.yml` YAML 문법 문제다. 여러 줄 문자열은 반드시 `run: |` 블록 들여쓰기 안에 둔다.
|
||||||
|
|
||||||
|
4. **job 실패 로그 확인**
|
||||||
|
```powershell
|
||||||
|
curl.exe -sS -H "Authorization: token $env:GITEA_TOKEN_TAXBAIK" `
|
||||||
|
"http://178.104.200.7/api/v1/repos/kjh2064/taxbaik/actions/jobs/{job_id}/logs"
|
||||||
|
```
|
||||||
|
빌드/테스트/배포/헬스체크 중 어느 단계인지 먼저 분리한다.
|
||||||
|
|
||||||
|
**이번 장애 원인 기록**:
|
||||||
|
- `deploy.yml`의 Telegram 여러 줄 메시지 일부가 YAML 블록 들여쓰기 밖에 있어 Gitea workflow 파서가 실패했다.
|
||||||
|
- 이후 배포 실행은 되었지만, 운영 `Authentication:*:ClientId`가 빈 값인데 OAuth provider를 무조건 등록해 `ClientId` 예외로 500이 발생했다.
|
||||||
|
- 외부 OAuth provider는 ClientId/ClientSecret이 모두 있을 때만 등록한다.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## 12. 문제 해결
|
## 12. 문제 해결
|
||||||
|
|
||||||
| 문제 | 해결 |
|
| 문제 | 해결 |
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
**온라인 세무 상담 플랫폼** | 블로그 SEO 최적화 | 전국 고객 확보
|
**온라인 세무 상담 플랫폼** | 블로그 SEO 최적화 | 전국 고객 확보
|
||||||
|
|
||||||
|
CI deploy trigger verification note.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 개요
|
## 개요
|
||||||
|
|||||||
@@ -26,10 +26,10 @@
|
|||||||
}
|
}
|
||||||
else if (activities.Count == 0)
|
else if (activities.Count == 0)
|
||||||
{
|
{
|
||||||
<div class="pa-6 text-center">
|
<MudAlert Severity="Severity.Info" Class="mt-4">
|
||||||
<MudIcon Icon="@Icons.Material.Filled.Timeline" Style="font-size:3rem; opacity:.3;" />
|
<MudIcon Icon="@Icons.Material.Filled.Timeline" Class="me-2" />
|
||||||
<MudText Class="mt-2 text-muted">상담 활동이 없습니다.</MudText>
|
상담 활동이 없습니다.
|
||||||
</div>
|
</MudAlert>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -33,10 +33,10 @@
|
|||||||
}
|
}
|
||||||
else if (contracts.Count == 0)
|
else if (contracts.Count == 0)
|
||||||
{
|
{
|
||||||
<div class="pa-6 text-center">
|
<MudAlert Severity="Severity.Info" Class="mt-4">
|
||||||
<MudIcon Icon="@Icons.Material.Filled.Description" Style="font-size:3rem; opacity:.3;" />
|
<MudIcon Icon="@Icons.Material.Filled.Description" Class="me-2" />
|
||||||
<MudText Class="mt-2 text-muted">계약이 없습니다.</MudText>
|
계약이 없습니다.
|
||||||
</div>
|
</MudAlert>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -26,10 +26,10 @@
|
|||||||
}
|
}
|
||||||
else if (revenues.Count == 0)
|
else if (revenues.Count == 0)
|
||||||
{
|
{
|
||||||
<div class="pa-6 text-center">
|
<MudAlert Severity="Severity.Info" Class="mt-4">
|
||||||
<MudIcon Icon="@Icons.Material.Filled.Payments" Style="font-size:3rem; opacity:.3;" />
|
<MudIcon Icon="@Icons.Material.Filled.Payments" Class="me-2" />
|
||||||
<MudText Class="mt-2 text-muted">청구 기록이 없습니다.</MudText>
|
청구 기록이 없습니다.
|
||||||
</div>
|
</MudAlert>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -29,10 +29,10 @@
|
|||||||
}
|
}
|
||||||
else if (schedules.Count == 0)
|
else if (schedules.Count == 0)
|
||||||
{
|
{
|
||||||
<div class="pa-6 text-center">
|
<MudAlert Severity="Severity.Info" Class="mt-4">
|
||||||
<MudIcon Icon="@Icons.Material.Filled.EventBusy" Style="font-size:3rem; opacity:.3;" />
|
<MudIcon Icon="@Icons.Material.Filled.EventBusy" Class="me-2" />
|
||||||
<MudText Class="mt-2 text-muted">신고 일정이 없습니다.</MudText>
|
신고 일정이 없습니다.
|
||||||
</div>
|
</MudAlert>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ public class AuthController : ControllerBase
|
|||||||
|
|
||||||
return Ok(new
|
return Ok(new
|
||||||
{
|
{
|
||||||
|
token = tokenPair.AccessToken,
|
||||||
accessToken = tokenPair.AccessToken,
|
accessToken = tokenPair.AccessToken,
|
||||||
refreshToken = tokenPair.RefreshToken,
|
refreshToken = tokenPair.RefreshToken,
|
||||||
expiresIn = tokenPair.ExpiresIn
|
expiresIn = tokenPair.ExpiresIn
|
||||||
@@ -45,6 +46,7 @@ public class AuthController : ControllerBase
|
|||||||
|
|
||||||
return Ok(new
|
return Ok(new
|
||||||
{
|
{
|
||||||
|
token = tokenPair.AccessToken,
|
||||||
accessToken = tokenPair.AccessToken,
|
accessToken = tokenPair.AccessToken,
|
||||||
refreshToken = tokenPair.RefreshToken,
|
refreshToken = tokenPair.RefreshToken,
|
||||||
expiresIn = tokenPair.ExpiresIn
|
expiresIn = tokenPair.ExpiresIn
|
||||||
|
|||||||
+31
-13
@@ -64,7 +64,7 @@ if (isProduction && jwtKey.Contains("dev-secret", StringComparison.OrdinalIgnore
|
|||||||
throw new InvalidOperationException("Production JWT SecretKey must not use the development default.");
|
throw new InvalidOperationException("Production JWT SecretKey must not use the development default.");
|
||||||
var key = Encoding.ASCII.GetBytes(jwtKey);
|
var key = Encoding.ASCII.GetBytes(jwtKey);
|
||||||
|
|
||||||
builder.Services.AddAuthentication(opts =>
|
var authenticationBuilder = builder.Services.AddAuthentication(opts =>
|
||||||
{
|
{
|
||||||
opts.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
|
opts.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
|
||||||
opts.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
|
opts.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
|
||||||
@@ -100,19 +100,30 @@ builder.Services.AddAuthentication(opts =>
|
|||||||
opts.Cookie.HttpOnly = true;
|
opts.Cookie.HttpOnly = true;
|
||||||
opts.Cookie.SameSite = SameSiteMode.Lax;
|
opts.Cookie.SameSite = SameSiteMode.Lax;
|
||||||
opts.Cookie.SecurePolicy = isProduction ? CookieSecurePolicy.Always : CookieSecurePolicy.SameAsRequest;
|
opts.Cookie.SecurePolicy = isProduction ? CookieSecurePolicy.Always : CookieSecurePolicy.SameAsRequest;
|
||||||
})
|
});
|
||||||
.AddGoogle(PortalOAuthDefaults.GoogleScheme, opts =>
|
|
||||||
|
var googleClientId = builder.Configuration["Authentication:Google:ClientId"];
|
||||||
|
var googleClientSecret = builder.Configuration["Authentication:Google:ClientSecret"];
|
||||||
|
if (!string.IsNullOrWhiteSpace(googleClientId) && !string.IsNullOrWhiteSpace(googleClientSecret))
|
||||||
|
{
|
||||||
|
authenticationBuilder.AddGoogle(PortalOAuthDefaults.GoogleScheme, opts =>
|
||||||
{
|
{
|
||||||
opts.SignInScheme = PortalOAuthDefaults.ExternalScheme;
|
opts.SignInScheme = PortalOAuthDefaults.ExternalScheme;
|
||||||
opts.ClientId = builder.Configuration["Authentication:Google:ClientId"] ?? "";
|
opts.ClientId = googleClientId;
|
||||||
opts.ClientSecret = builder.Configuration["Authentication:Google:ClientSecret"] ?? "";
|
opts.ClientSecret = googleClientSecret;
|
||||||
opts.CallbackPath = "/taxbaik/portal/signin-google";
|
opts.CallbackPath = "/taxbaik/portal/signin-google";
|
||||||
})
|
});
|
||||||
.AddOAuth(PortalOAuthDefaults.NaverScheme, opts =>
|
}
|
||||||
|
|
||||||
|
var naverClientId = builder.Configuration["Authentication:Naver:ClientId"];
|
||||||
|
var naverClientSecret = builder.Configuration["Authentication:Naver:ClientSecret"];
|
||||||
|
if (!string.IsNullOrWhiteSpace(naverClientId) && !string.IsNullOrWhiteSpace(naverClientSecret))
|
||||||
|
{
|
||||||
|
authenticationBuilder.AddOAuth(PortalOAuthDefaults.NaverScheme, opts =>
|
||||||
{
|
{
|
||||||
opts.SignInScheme = PortalOAuthDefaults.ExternalScheme;
|
opts.SignInScheme = PortalOAuthDefaults.ExternalScheme;
|
||||||
opts.ClientId = builder.Configuration["Authentication:Naver:ClientId"] ?? "";
|
opts.ClientId = naverClientId;
|
||||||
opts.ClientSecret = builder.Configuration["Authentication:Naver:ClientSecret"] ?? "";
|
opts.ClientSecret = naverClientSecret;
|
||||||
opts.CallbackPath = "/taxbaik/portal/signin-naver";
|
opts.CallbackPath = "/taxbaik/portal/signin-naver";
|
||||||
opts.AuthorizationEndpoint = "https://nid.naver.com/oauth2.0/authorize";
|
opts.AuthorizationEndpoint = "https://nid.naver.com/oauth2.0/authorize";
|
||||||
opts.TokenEndpoint = "https://nid.naver.com/oauth2.0/token";
|
opts.TokenEndpoint = "https://nid.naver.com/oauth2.0/token";
|
||||||
@@ -133,12 +144,18 @@ builder.Services.AddAuthentication(opts =>
|
|||||||
context.Identity?.AddClaim(new System.Security.Claims.Claim(System.Security.Claims.ClaimTypes.Email, responseRoot.GetProperty("email").GetString() ?? ""));
|
context.Identity?.AddClaim(new System.Security.Claims.Claim(System.Security.Claims.ClaimTypes.Email, responseRoot.GetProperty("email").GetString() ?? ""));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
})
|
});
|
||||||
.AddOAuth(PortalOAuthDefaults.KakaoScheme, opts =>
|
}
|
||||||
|
|
||||||
|
var kakaoClientId = builder.Configuration["Authentication:Kakao:ClientId"];
|
||||||
|
var kakaoClientSecret = builder.Configuration["Authentication:Kakao:ClientSecret"];
|
||||||
|
if (!string.IsNullOrWhiteSpace(kakaoClientId) && !string.IsNullOrWhiteSpace(kakaoClientSecret))
|
||||||
|
{
|
||||||
|
authenticationBuilder.AddOAuth(PortalOAuthDefaults.KakaoScheme, opts =>
|
||||||
{
|
{
|
||||||
opts.SignInScheme = PortalOAuthDefaults.ExternalScheme;
|
opts.SignInScheme = PortalOAuthDefaults.ExternalScheme;
|
||||||
opts.ClientId = builder.Configuration["Authentication:Kakao:ClientId"] ?? "";
|
opts.ClientId = kakaoClientId;
|
||||||
opts.ClientSecret = builder.Configuration["Authentication:Kakao:ClientSecret"] ?? "";
|
opts.ClientSecret = kakaoClientSecret;
|
||||||
opts.CallbackPath = "/taxbaik/portal/signin-kakao";
|
opts.CallbackPath = "/taxbaik/portal/signin-kakao";
|
||||||
opts.AuthorizationEndpoint = "https://kauth.kakao.com/oauth/authorize";
|
opts.AuthorizationEndpoint = "https://kauth.kakao.com/oauth/authorize";
|
||||||
opts.TokenEndpoint = "https://kauth.kakao.com/oauth/token";
|
opts.TokenEndpoint = "https://kauth.kakao.com/oauth/token";
|
||||||
@@ -162,6 +179,7 @@ builder.Services.AddAuthentication(opts =>
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Blazor 인증
|
// Blazor 인증
|
||||||
builder.Services.AddScoped<AuthService>();
|
builder.Services.AddScoped<AuthService>();
|
||||||
|
|||||||
@@ -15,75 +15,55 @@ test.describe('admin CRM pages', () => {
|
|||||||
await navigateInBlazor(page, `${baseUrl}/admin/tax-profiles`);
|
await navigateInBlazor(page, `${baseUrl}/admin/tax-profiles`);
|
||||||
await expect(page).toHaveURL(/\/admin\/tax-profiles$/);
|
await expect(page).toHaveURL(/\/admin\/tax-profiles$/);
|
||||||
|
|
||||||
// 제목 확인
|
await expect(page.locator('.admin-page-title')).toHaveText('세무 프로필', { timeout: 15_000 });
|
||||||
await expect(page.getByText('세무 프로필 관리')).toBeVisible({ timeout: 15_000 });
|
|
||||||
|
|
||||||
// 새 프로필 추가 버튼 확인
|
|
||||||
await expect(page.getByRole('button', { name: /새 프로필 추가/ })).toBeVisible();
|
await expect(page.getByRole('button', { name: /새 프로필 추가/ })).toBeVisible();
|
||||||
|
|
||||||
// MudDataGrid 로드 확인 (테이블 or 비어있음 메시지)
|
await expect(page.locator('.admin-grid, .mud-alert')).toBeVisible({ timeout: 15_000 });
|
||||||
const gridOrEmpty = page.locator('.admin-grid, .mud-alert');
|
|
||||||
await expect(gridOrEmpty).toBeVisible({ timeout: 15_000 });
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('TaxFilingSchedules page loads with D-day tracking', async ({ page }) => {
|
test('TaxFilingSchedules page loads with D-day tracking', async ({ page }) => {
|
||||||
await navigateInBlazor(page, `${baseUrl}/admin/tax-filing-schedules`);
|
await navigateInBlazor(page, `${baseUrl}/admin/tax-filing-schedules`);
|
||||||
await expect(page).toHaveURL(/\/admin\/tax-filing-schedules$/);
|
await expect(page).toHaveURL(/\/admin\/tax-filing-schedules$/);
|
||||||
|
|
||||||
// 제목 확인
|
await expect(page.locator('.admin-page-title')).toHaveText('신고 일정', { timeout: 15_000 });
|
||||||
await expect(page.getByText('신고 일정 관리')).toBeVisible({ timeout: 15_000 });
|
|
||||||
|
|
||||||
// 새 일정 추가 버튼
|
|
||||||
await expect(page.getByRole('button', { name: /새 일정 추가/ })).toBeVisible();
|
await expect(page.getByRole('button', { name: /새 일정 추가/ })).toBeVisible();
|
||||||
|
|
||||||
// 그리드 로드
|
await expect(page.locator('.admin-grid, .mud-alert')).toBeVisible({ timeout: 15_000 });
|
||||||
const gridOrEmpty = page.locator('.admin-grid, .mud-alert');
|
|
||||||
await expect(gridOrEmpty).toBeVisible({ timeout: 15_000 });
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Contracts page loads with MRR display', async ({ page }) => {
|
test('Contracts page loads with MRR display', async ({ page }) => {
|
||||||
await navigateInBlazor(page, `${baseUrl}/admin/contracts`);
|
await navigateInBlazor(page, `${baseUrl}/admin/contracts`);
|
||||||
await expect(page).toHaveURL(/\/admin\/contracts$/);
|
await expect(page).toHaveURL(/\/admin\/contracts$/);
|
||||||
|
|
||||||
// 제목 확인
|
await expect(page.locator('.admin-page-title')).toHaveText('계약 관리', { timeout: 15_000 });
|
||||||
await expect(page.getByText('계약 관리')).toBeVisible({ timeout: 15_000 });
|
|
||||||
|
|
||||||
// 새 계약 추가 버튼
|
|
||||||
await expect(page.getByRole('button', { name: /새 계약 추가/ })).toBeVisible();
|
await expect(page.getByRole('button', { name: /새 계약 추가/ })).toBeVisible();
|
||||||
|
|
||||||
// 그리드 로드
|
await expect(page.locator('.admin-grid, .mud-alert')).toBeVisible({ timeout: 15_000 });
|
||||||
const gridOrEmpty = page.locator('.admin-grid, .mud-alert');
|
|
||||||
await expect(gridOrEmpty).toBeVisible({ timeout: 15_000 });
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('ConsultingActivities page loads with activity records', async ({ page }) => {
|
test('ConsultingActivities page loads with activity records', async ({ page }) => {
|
||||||
await navigateInBlazor(page, `${baseUrl}/admin/consulting-activities`);
|
await navigateInBlazor(page, `${baseUrl}/admin/consulting-activities`);
|
||||||
await expect(page).toHaveURL(/\/admin\/consulting-activities$/);
|
await expect(page).toHaveURL(/\/admin\/consulting-activities$/);
|
||||||
|
|
||||||
// 제목 확인
|
await expect(page.locator('.admin-page-title')).toHaveText('상담 활동 관리', { timeout: 15_000 });
|
||||||
await expect(page.getByText('상담 활동 관리')).toBeVisible({ timeout: 15_000 });
|
|
||||||
|
|
||||||
// 새 활동 기록 버튼
|
|
||||||
await expect(page.getByRole('button', { name: /새 활동 기록/ })).toBeVisible();
|
await expect(page.getByRole('button', { name: /새 활동 기록/ })).toBeVisible();
|
||||||
|
|
||||||
// 그리드 로드
|
await expect(page.locator('.admin-grid, .mud-alert')).toBeVisible({ timeout: 15_000 });
|
||||||
const gridOrEmpty = page.locator('.admin-grid, .mud-alert');
|
|
||||||
await expect(gridOrEmpty).toBeVisible({ timeout: 15_000 });
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('RevenueTrackings page loads with payment status tracking', async ({ page }) => {
|
test('RevenueTrackings page loads with payment status tracking', async ({ page }) => {
|
||||||
await navigateInBlazor(page, `${baseUrl}/admin/revenue-trackings`);
|
await navigateInBlazor(page, `${baseUrl}/admin/revenue-trackings`);
|
||||||
await expect(page).toHaveURL(/\/admin\/revenue-trackings$/);
|
await expect(page).toHaveURL(/\/admin\/revenue-trackings$/);
|
||||||
|
|
||||||
// 제목 확인
|
await expect(page.locator('.admin-page-title')).toHaveText('수익 추적 관리', { timeout: 15_000 });
|
||||||
await expect(page.getByText('수익 추적 관리')).toBeVisible({ timeout: 15_000 });
|
|
||||||
|
|
||||||
// 새 청구 추가 버튼
|
|
||||||
await expect(page.getByRole('button', { name: /새 청구 추가/ })).toBeVisible();
|
await expect(page.getByRole('button', { name: /새 청구 추가/ })).toBeVisible();
|
||||||
|
|
||||||
// 그리드 로드
|
await expect(page.locator('.admin-grid, .mud-alert')).toBeVisible({ timeout: 15_000 });
|
||||||
const gridOrEmpty = page.locator('.admin-grid, .mud-alert');
|
|
||||||
await expect(gridOrEmpty).toBeVisible({ timeout: 15_000 });
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('CRM navigation group is visible and expandable', async ({ page }) => {
|
test('CRM navigation group is visible and expandable', async ({ page }) => {
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ test.describe('admin authentication', () => {
|
|||||||
await page.getByRole('button', { name: '로그인' }).click();
|
await page.getByRole('button', { name: '로그인' }).click();
|
||||||
|
|
||||||
await expect(page).toHaveURL(/\/taxbaik\/admin\/dashboard$/);
|
await expect(page).toHaveURL(/\/taxbaik\/admin\/dashboard$/);
|
||||||
await expect(page.getByRole('heading', { name: '대시보드' })).toBeVisible({ timeout: 20_000 });
|
await expect(page.getByRole('heading', { name: '대시보드' }).first()).toBeVisible({ timeout: 20_000 });
|
||||||
await expect(page.getByRole('link', { name: /로그아웃/ })).toBeVisible();
|
await expect(page.getByRole('link', { name: /로그아웃/ })).toBeVisible();
|
||||||
expect(consoleErrors, 'browser console/page errors').toEqual([]);
|
expect(consoleErrors, 'browser console/page errors').toEqual([]);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ export async function loginThroughAdminUi(
|
|||||||
await page.locator('input[placeholder="비밀번호"]').fill(password);
|
await page.locator('input[placeholder="비밀번호"]').fill(password);
|
||||||
await page.getByRole('button', { name: '로그인' }).click();
|
await page.getByRole('button', { name: '로그인' }).click();
|
||||||
await expect(page).toHaveURL(/\/taxbaik\/admin\/dashboard$/);
|
await expect(page).toHaveURL(/\/taxbaik\/admin\/dashboard$/);
|
||||||
await expect(page.getByRole('heading', { name: '대시보드' })).toBeVisible({ timeout: 20_000 });
|
await expect(page.getByRole('heading', { name: '대시보드' }).first()).toBeVisible({ timeout: 20_000 });
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function navigateInBlazor(page: Page, targetUrl: string) {
|
export async function navigateInBlazor(page: Page, targetUrl: string) {
|
||||||
|
|||||||
@@ -39,9 +39,11 @@ test.describe('inquiry detail', () => {
|
|||||||
await expect(page.getByText(phone, { exact: true }).first()).toBeVisible();
|
await expect(page.getByText(phone, { exact: true }).first()).toBeVisible();
|
||||||
await expect(page.getByText(message, { exact: true }).first()).toBeVisible();
|
await expect(page.getByText(message, { exact: true }).first()).toBeVisible();
|
||||||
await expect(page.getByRole('button', { name: '신규' })).toBeVisible();
|
await expect(page.getByRole('button', { name: '신규' })).toBeVisible();
|
||||||
await expect(page.getByRole('button', { name: '연락함' })).toBeVisible();
|
await expect(page.getByRole('button', { name: '상담중' })).toBeVisible();
|
||||||
await expect(page.getByRole('button', { name: '완료' })).toBeVisible();
|
await expect(page.getByRole('button', { name: '계약완료' })).toBeVisible();
|
||||||
await expect(page.getByRole('button', { name: '문의 목록으로 돌아가기' })).toBeVisible();
|
await expect(page.getByRole('button', { name: '거절' })).toBeVisible();
|
||||||
await expect(page.getByRole('link', { name: '다른 문의도 보기' })).toBeVisible();
|
await expect(page.getByRole('button', { name: '종결' })).toBeVisible();
|
||||||
|
await expect(page.getByRole('button', { name: '문의 목록으로' })).toBeVisible();
|
||||||
|
await expect(page.getByRole('button', { name: '고객으로 등록' })).toBeVisible();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user