forked from baron/baron-sso
코드체크 업데이트
This commit is contained in:
@@ -174,7 +174,7 @@ jobs:
|
||||
# 코드 변경 반영을 위해 build 수행 (userfront nginx.conf 등)
|
||||
docker compose -f staging_pull_compose.yaml build --pull
|
||||
|
||||
docker compose -f staging_pull_compose.yaml up -d --remove-orphans
|
||||
docker compose -f staging_pull_compose.yaml up -d --remove-orphans --renew-anon-volumes
|
||||
docker compose -f staging_pull_compose.yaml up -d --force-recreate kratos hydra keto oathkeeper
|
||||
docker compose -f staging_pull_compose.yaml up -d --force-recreate ory_stack_check
|
||||
docker compose -f staging_pull_compose.yaml up -d init-rp
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
import { createRequire } from "node:module";
|
||||
import { defineConfig, devices } from "@playwright/test";
|
||||
|
||||
const require = createRequire(import.meta.url);
|
||||
const { shouldIncludeWebKit } =
|
||||
require("../scripts/playwrightHostDeps.cjs") as {
|
||||
shouldIncludeWebKit: () => boolean;
|
||||
};
|
||||
|
||||
const configuredWorkers = process.env.PLAYWRIGHT_WORKERS
|
||||
? Number.parseInt(process.env.PLAYWRIGHT_WORKERS, 10)
|
||||
: undefined;
|
||||
@@ -57,10 +64,14 @@ export default defineConfig({
|
||||
use: { ...devices["Desktop Firefox"] },
|
||||
},
|
||||
|
||||
...(shouldIncludeWebKit()
|
||||
? [
|
||||
{
|
||||
name: "webkit",
|
||||
use: { ...devices["Desktop Safari"] },
|
||||
},
|
||||
]
|
||||
: []),
|
||||
],
|
||||
|
||||
/* Run your local dev server before starting the tests */
|
||||
|
||||
@@ -35,6 +35,29 @@ if [ "${1:-}" = "--print-mode" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
ensure_frontend_dependencies() {
|
||||
if [ ! -f package.json ] || [ ! -f package-lock.json ]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
if command -v sha256sum >/dev/null 2>&1; then
|
||||
deps_hash="$(sha256sum package.json package-lock.json | sha256sum | awk '{print $1}')"
|
||||
else
|
||||
deps_hash="$(cksum package.json package-lock.json | cksum | awk '{print $1}')"
|
||||
fi
|
||||
deps_stamp="node_modules/.baron-deps-hash"
|
||||
installed_hash="$(cat "$deps_stamp" 2>/dev/null || true)"
|
||||
|
||||
if [ "$installed_hash" != "$deps_hash" ]; then
|
||||
echo "Installing frontend dependencies from package-lock.json..."
|
||||
npm ci
|
||||
mkdir -p node_modules
|
||||
printf '%s\n' "$deps_hash" > "$deps_stamp"
|
||||
fi
|
||||
}
|
||||
|
||||
ensure_frontend_dependencies
|
||||
|
||||
if [ "$mode" = "production" ]; then
|
||||
echo "Running in production mode with Vite preview..."
|
||||
exec sh -c "npm run build && npm run preview -- --host 0.0.0.0"
|
||||
|
||||
@@ -101,18 +101,10 @@ export function ParentTenantSelector({
|
||||
return (
|
||||
<div className="space-y-2">
|
||||
<div className="flex min-h-8 flex-wrap items-center justify-between gap-2">
|
||||
<Label className="text-sm font-semibold">
|
||||
{label}
|
||||
</Label>
|
||||
<Label className="text-sm font-semibold">{label}</Label>
|
||||
{labelAction}
|
||||
</div>
|
||||
<input
|
||||
id={id}
|
||||
name={id}
|
||||
type="hidden"
|
||||
value={value}
|
||||
readOnly
|
||||
/>
|
||||
<input id={id} name={id} type="hidden" value={value} readOnly />
|
||||
<div className="flex min-h-10 flex-wrap items-center gap-2 rounded-md border border-input bg-background px-3 py-2">
|
||||
<Button
|
||||
type="button"
|
||||
|
||||
@@ -63,7 +63,10 @@ function TenantCreatePage() {
|
||||
parentStepConfirmed || Boolean(selectedParentTenant);
|
||||
const parentContextLabel = selectedParentTenant
|
||||
? canConfigureHanmacOrg
|
||||
? t("ui.admin.tenants.create.parent_context.hanmac", "한맥가족 하위 테넌트")
|
||||
? t(
|
||||
"ui.admin.tenants.create.parent_context.hanmac",
|
||||
"한맥가족 하위 테넌트",
|
||||
)
|
||||
: t("ui.admin.tenants.create.parent_context.general", "일반 하위 테넌트")
|
||||
: parentStepConfirmed
|
||||
? t("ui.admin.tenants.create.parent_context.root", "최상위 테넌트")
|
||||
@@ -232,10 +235,7 @@ function TenantCreatePage() {
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
<div
|
||||
data-testid="tenant-visibility-slot"
|
||||
className="space-y-2"
|
||||
>
|
||||
<div data-testid="tenant-visibility-slot" className="space-y-2">
|
||||
<Label
|
||||
htmlFor="tenant-visibility"
|
||||
className="text-sm font-semibold"
|
||||
|
||||
@@ -980,7 +980,7 @@ function TenantListPage() {
|
||||
</Button>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}{" "}
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
|
||||
@@ -338,10 +338,7 @@ export function TenantProfilePage() {
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
<div
|
||||
data-testid="tenant-visibility-slot"
|
||||
className="space-y-2"
|
||||
>
|
||||
<div data-testid="tenant-visibility-slot" className="space-y-2">
|
||||
<Label className="text-sm font-semibold">
|
||||
{t("ui.admin.tenants.profile.visibility", "공개 범위")}
|
||||
</Label>
|
||||
|
||||
@@ -202,6 +202,11 @@ subtitle = "Subtitle"
|
||||
[msg.admin.tenants.import_preview]
|
||||
description = "Rows without tenant_id are compared with existing tenant candidates, then imported as new tenants or updates."
|
||||
|
||||
[msg.admin.tenants.parent]
|
||||
local_picker_description = "Select the tenant to use as the parent from the tenant list."
|
||||
local_picker_empty = "No selectable tenants are available."
|
||||
picker_description = "Select a tenant in org-chart to apply it as the parent tenant."
|
||||
|
||||
[msg.admin.tenants.admins]
|
||||
add_success = "Add Success"
|
||||
empty = "Empty"
|
||||
@@ -217,6 +222,7 @@ remove_success = "Owner permission revoked."
|
||||
subtitle = "List of owners with top-level permissions for this tenant."
|
||||
|
||||
[msg.admin.tenants.create]
|
||||
pick_parent_first = "Select the parent tenant first."
|
||||
subtitle = "Subtitle"
|
||||
|
||||
[msg.admin.tenants.create.form]
|
||||
@@ -908,9 +914,14 @@ title = "Domain conflict"
|
||||
candidates = "Candidates"
|
||||
confirm = "Run import"
|
||||
create_new_reset = "Create new (reset ID/slug)"
|
||||
csv_parents = "CSV Parents"
|
||||
external_id = "External ID"
|
||||
match = "Match"
|
||||
no_candidates = "No candidates"
|
||||
parent = "Parent"
|
||||
parent_companies = "Parent Companies"
|
||||
parent_company_groups = "Parent Company Groups"
|
||||
parent_organizations = "Parent Organizations"
|
||||
parent_unresolved = "Parent needs review"
|
||||
slug_exists = "slug conflict"
|
||||
title = "Confirm CSV import"
|
||||
@@ -957,11 +968,20 @@ domains_label = "Allowed Domains (Comma separated)"
|
||||
domains_placeholder = "example.com, example.kr"
|
||||
name = "Tenant name"
|
||||
parent = "Parent"
|
||||
pick_hanmac_parent = "Pick from Hanmac Family"
|
||||
pick_other_parent = "Pick another tenant"
|
||||
root_tenant = "Create as top-level tenant"
|
||||
slug = "Slug"
|
||||
slug_placeholder = "tenant-slug"
|
||||
status = "Status"
|
||||
type = "Type"
|
||||
|
||||
[ui.admin.tenants.create.parent_context]
|
||||
general = "General child tenant"
|
||||
hanmac = "Hanmac Family child tenant"
|
||||
pick_required = "Parent tenant selection required"
|
||||
root = "Top-level tenant"
|
||||
|
||||
[ui.admin.tenants.create.memo]
|
||||
title = "Title"
|
||||
|
||||
@@ -1023,11 +1043,17 @@ allowed_domains_help = "Users with these email domains will be automatically ass
|
||||
approve_button = "Approve Tenant"
|
||||
description = "Description"
|
||||
name = "Tenant Name"
|
||||
org_unit_type = "Organization detail type"
|
||||
slug = "Slug"
|
||||
status = "Status"
|
||||
subtitle = "Slug and status changes are applied immediately."
|
||||
title = "Tenant Profile"
|
||||
type = "Type"
|
||||
visibility = "Visibility"
|
||||
|
||||
[ui.admin.tenants.parent]
|
||||
local_search_placeholder = "Search tenant name or slug"
|
||||
pick_tenant = "Pick tenant"
|
||||
|
||||
[ui.admin.tenants.registry]
|
||||
title = "Tenant registry"
|
||||
|
||||
@@ -203,6 +203,11 @@ subtitle = "현재 등록된 테넌트를 확인하고 상태를 관리합니다
|
||||
[msg.admin.tenants.import_preview]
|
||||
description = "tenant_id가 없는 행은 기존 테넌트 후보와 비교한 뒤 신규 생성 또는 기존 테넌트 갱신으로 처리합니다."
|
||||
|
||||
[msg.admin.tenants.parent]
|
||||
local_picker_description = "테넌트 목록에서 상위 테넌트로 사용할 항목을 선택합니다."
|
||||
local_picker_empty = "선택할 수 있는 테넌트가 없습니다."
|
||||
picker_description = "org-chart에서 테넌트를 선택하면 상위 테넌트에 반영됩니다."
|
||||
|
||||
[msg.admin.tenants.admins]
|
||||
add_success = "관리자가 추가되었습니다."
|
||||
empty = "등록된 관리자가 없습니다."
|
||||
@@ -218,6 +223,7 @@ remove_success = "소유자 권한이 회수되었습니다."
|
||||
subtitle = "이 테넌트의 최상위 권한을 가진 소유자(조직장) 목록입니다."
|
||||
|
||||
[msg.admin.tenants.create]
|
||||
pick_parent_first = "상위 테넌트를 먼저 선택하세요."
|
||||
subtitle = "글로벌 운영 기준의 신규 테넌트를 등록합니다."
|
||||
|
||||
[msg.admin.tenants.create.form]
|
||||
@@ -910,9 +916,14 @@ title = "도메인 충돌"
|
||||
candidates = "후보"
|
||||
confirm = "가져오기 실행"
|
||||
create_new_reset = "신규 생성 (ID/slug 재설정)"
|
||||
csv_parents = "CSV 상위 테넌트"
|
||||
external_id = "외부 ID"
|
||||
match = "매칭"
|
||||
no_candidates = "후보 없음"
|
||||
parent = "상위"
|
||||
parent_companies = "상위 회사"
|
||||
parent_company_groups = "상위 그룹사"
|
||||
parent_organizations = "상위 조직"
|
||||
parent_unresolved = "부모 확인 필요"
|
||||
slug_exists = "slug 충돌"
|
||||
title = "CSV 가져오기 확인"
|
||||
@@ -959,11 +970,20 @@ domains_label = "Allowed Domains (Comma separated)"
|
||||
domains_placeholder = "example.com, example.kr"
|
||||
name = "테넌트 이름"
|
||||
parent = "상위 테넌트"
|
||||
pick_hanmac_parent = "한맥가족에서 선택"
|
||||
pick_other_parent = "다른 테넌트 선택"
|
||||
root_tenant = "최상위 테넌트로 생성"
|
||||
slug = "Slug"
|
||||
slug_placeholder = "tenant-slug"
|
||||
status = "상태"
|
||||
type = "유형"
|
||||
|
||||
[ui.admin.tenants.create.parent_context]
|
||||
general = "일반 하위 테넌트"
|
||||
hanmac = "한맥가족 하위 테넌트"
|
||||
pick_required = "상위 테넌트 선택 필요"
|
||||
root = "최상위 테넌트"
|
||||
|
||||
[ui.admin.tenants.create.memo]
|
||||
title = "정책 메모"
|
||||
|
||||
@@ -1025,11 +1045,17 @@ allowed_domains_help = "이 도메인을 가진 이메일로 가입한 사용자
|
||||
approve_button = "테넌트 승인"
|
||||
description = "설명"
|
||||
name = "테넌트 이름"
|
||||
org_unit_type = "조직 세부타입"
|
||||
slug = "슬러그 (Slug)"
|
||||
status = "상태"
|
||||
subtitle = "슬러그 및 상태 변경은 즉시 적용됩니다."
|
||||
title = "테넌트 프로필"
|
||||
type = "테넌트 유형"
|
||||
visibility = "공개 범위"
|
||||
|
||||
[ui.admin.tenants.parent]
|
||||
local_search_placeholder = "테넌트 이름 또는 슬러그 검색"
|
||||
pick_tenant = "테넌트 선택"
|
||||
|
||||
[ui.admin.tenants.registry]
|
||||
title = "Tenant registry"
|
||||
|
||||
@@ -208,6 +208,11 @@ subtitle = ""
|
||||
[msg.admin.tenants.import_preview]
|
||||
description = ""
|
||||
|
||||
[msg.admin.tenants.parent]
|
||||
local_picker_description = ""
|
||||
local_picker_empty = ""
|
||||
picker_description = ""
|
||||
|
||||
[msg.admin.tenants.admins]
|
||||
add_success = ""
|
||||
empty = ""
|
||||
@@ -223,6 +228,7 @@ remove_success = ""
|
||||
subtitle = ""
|
||||
|
||||
[msg.admin.tenants.create]
|
||||
pick_parent_first = ""
|
||||
subtitle = ""
|
||||
|
||||
[msg.admin.tenants.create.form]
|
||||
@@ -924,9 +930,14 @@ title = ""
|
||||
candidates = ""
|
||||
confirm = ""
|
||||
create_new_reset = ""
|
||||
csv_parents = ""
|
||||
external_id = ""
|
||||
match = ""
|
||||
no_candidates = ""
|
||||
parent = ""
|
||||
parent_companies = ""
|
||||
parent_company_groups = ""
|
||||
parent_organizations = ""
|
||||
parent_unresolved = ""
|
||||
slug_exists = ""
|
||||
title = ""
|
||||
@@ -973,11 +984,20 @@ domains_label = ""
|
||||
domains_placeholder = ""
|
||||
name = ""
|
||||
parent = ""
|
||||
pick_hanmac_parent = ""
|
||||
pick_other_parent = ""
|
||||
root_tenant = ""
|
||||
slug = ""
|
||||
slug_placeholder = ""
|
||||
status = ""
|
||||
type = ""
|
||||
|
||||
[ui.admin.tenants.create.parent_context]
|
||||
general = ""
|
||||
hanmac = ""
|
||||
pick_required = ""
|
||||
root = ""
|
||||
|
||||
[ui.admin.tenants.create.memo]
|
||||
title = ""
|
||||
|
||||
@@ -1044,11 +1064,17 @@ allowed_domains_help = ""
|
||||
approve_button = ""
|
||||
description = ""
|
||||
name = ""
|
||||
org_unit_type = ""
|
||||
slug = ""
|
||||
status = ""
|
||||
subtitle = ""
|
||||
title = ""
|
||||
type = ""
|
||||
visibility = ""
|
||||
|
||||
[ui.admin.tenants.parent]
|
||||
local_search_placeholder = ""
|
||||
pick_tenant = ""
|
||||
|
||||
[ui.admin.tenants.registry]
|
||||
title = ""
|
||||
|
||||
@@ -110,9 +110,7 @@ test.describe("Tenants Management", () => {
|
||||
await expect(page.locator("h2").last()).toContainText(/추가|Create/i, {
|
||||
timeout: 20000,
|
||||
});
|
||||
await page
|
||||
.getByRole("button", { name: "최상위 테넌트로 생성" })
|
||||
.click();
|
||||
await page.getByRole("button", { name: "최상위 테넌트로 생성" }).click();
|
||||
|
||||
const nameInput = page.locator('input[name="name"]').first();
|
||||
await nameInput.fill("New Tenant");
|
||||
@@ -213,9 +211,7 @@ test.describe("Tenants Management", () => {
|
||||
{
|
||||
type: "orgfront:picker:confirm",
|
||||
payload: {
|
||||
selections: [
|
||||
{ type: "tenant", id: "family-1", name: "한맥가족" },
|
||||
],
|
||||
selections: [{ type: "tenant", id: "family-1", name: "한맥가족" }],
|
||||
},
|
||||
},
|
||||
window.location.origin,
|
||||
@@ -260,7 +256,12 @@ test.describe("Tenants Management", () => {
|
||||
const headers = { "Access-Control-Allow-Origin": "*" };
|
||||
if (method === "GET") {
|
||||
return route.fulfill({
|
||||
json: { items: tenants, total: tenants.length, limit: 1000, offset: 0 },
|
||||
json: {
|
||||
items: tenants,
|
||||
total: tenants.length,
|
||||
limit: 1000,
|
||||
offset: 0,
|
||||
},
|
||||
headers,
|
||||
});
|
||||
}
|
||||
@@ -286,9 +287,7 @@ test.describe("Tenants Management", () => {
|
||||
{
|
||||
type: "orgfront:picker:confirm",
|
||||
payload: {
|
||||
selections: [
|
||||
{ type: "tenant", id: "family-1", name: "한맥가족" },
|
||||
],
|
||||
selections: [{ type: "tenant", id: "family-1", name: "한맥가족" }],
|
||||
},
|
||||
},
|
||||
window.location.origin,
|
||||
@@ -309,8 +308,8 @@ test.describe("Tenants Management", () => {
|
||||
const visibilityWidth = await page
|
||||
.getByTestId("tenant-visibility-slot")
|
||||
.evaluate((element) => element.getBoundingClientRect().width);
|
||||
const columns = await layout.evaluate((element) =>
|
||||
window.getComputedStyle(element).gridTemplateColumns,
|
||||
const columns = await layout.evaluate(
|
||||
(element) => window.getComputedStyle(element).gridTemplateColumns,
|
||||
);
|
||||
expect(columns.split(" ").length).toBe(4);
|
||||
expect(parentWidth).toBeGreaterThan(orgUnitWidth * 1.7);
|
||||
@@ -543,9 +542,7 @@ test.describe("Tenants Management", () => {
|
||||
await expect(page.locator("h2").last()).toContainText(/추가|Create/i, {
|
||||
timeout: 20000,
|
||||
});
|
||||
await page
|
||||
.getByRole("button", { name: "최상위 테넌트로 생성" })
|
||||
.click();
|
||||
await page.getByRole("button", { name: "최상위 테넌트로 생성" }).click();
|
||||
|
||||
const submitBtn = page.getByRole("button", { name: /^생성$/ });
|
||||
await expect(submitBtn).toBeDisabled();
|
||||
@@ -715,8 +712,8 @@ test.describe("Tenants Management", () => {
|
||||
await expect(layout).toContainText("조직 세부타입");
|
||||
await expect(layout).toContainText("공개 범위");
|
||||
|
||||
const columns = await layout.evaluate((element) =>
|
||||
window.getComputedStyle(element).gridTemplateColumns,
|
||||
const columns = await layout.evaluate(
|
||||
(element) => window.getComputedStyle(element).gridTemplateColumns,
|
||||
);
|
||||
expect(columns.split(" ").length).toBe(4);
|
||||
|
||||
|
||||
@@ -419,6 +419,8 @@ func TestHeadlessPasswordLogin_E2E_ResponseIncludesDetailedCodeAndLogs(t *testin
|
||||
}
|
||||
|
||||
func TestHeadlessPasswordLogin_E2E_DebugLogsIncludeDiagnostics(t *testing.T) {
|
||||
t.Setenv("BACKEND_PUBLIC_URL", "")
|
||||
|
||||
privateKey, jwks := mustE2EHeadlessRSAJWK(t)
|
||||
const receivedAudience = "https://sso.hmac.kr/api/v1/auth/headless/password/login"
|
||||
clientAssertion := mustE2EHeadlessClientAssertion(
|
||||
@@ -458,6 +460,8 @@ func TestHeadlessPasswordLogin_E2E_DebugLogsIncludeDiagnostics(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestHeadlessPasswordLogin_E2E_AcceptsForwardedHTTPSAudience(t *testing.T) {
|
||||
t.Setenv("BACKEND_PUBLIC_URL", "")
|
||||
|
||||
privateKey, jwks := mustE2EHeadlessRSAJWK(t)
|
||||
const receivedAudience = "https://sso.hmac.kr/api/v1/auth/headless/password/login"
|
||||
clientAssertion := mustE2EHeadlessClientAssertion(
|
||||
|
||||
@@ -894,6 +894,8 @@ func TestHeadlessPasswordLogin_HeadlessLoginClientSuccess(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestHeadlessPasswordLogin_AuditIncludesClientMetadata(t *testing.T) {
|
||||
t.Setenv("BACKEND_PUBLIC_URL", "")
|
||||
|
||||
mockIdp := new(MockIdentityProvider)
|
||||
mockIdp.On("SignIn", "employee001", "password").Return(&domain.AuthInfo{
|
||||
SessionToken: &domain.Token{JWT: "valid-jwt", SessionID: "session-123"},
|
||||
|
||||
@@ -11,6 +11,8 @@ import (
|
||||
)
|
||||
|
||||
func TestRPManifestJSONIncludesIAMAndExternalKeyContract(t *testing.T) {
|
||||
t.Setenv("BACKEND_PUBLIC_URL", "")
|
||||
|
||||
app := fiber.New()
|
||||
h := NewRPManifestHandler()
|
||||
app.Get("/.well-known/baron-rp-manifest.json", h.GetJSON)
|
||||
|
||||
@@ -112,6 +112,7 @@ func (m *MockUserRepoForHandler) Delete(ctx context.Context, id string) error {
|
||||
m.deletedIDs = append(m.deletedIDs, id)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *MockUserRepoForHandler) FindByEmail(ctx context.Context, email string) (*domain.User, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
@@ -55,6 +55,7 @@ func (m *MockUserRepository) Update(ctx context.Context, user *domain.User) erro
|
||||
m.updatedUsers = append(m.updatedUsers, copied)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *MockUserRepository) Delete(ctx context.Context, id string) error {
|
||||
return m.Called(ctx, id).Error(0)
|
||||
}
|
||||
|
||||
@@ -213,6 +213,8 @@ func TestResolveWorksmobileDomainIDFromTenantIgnoresRootDomainMappings(t *testin
|
||||
}
|
||||
|
||||
func TestResolveWorksmobileDomainIDFromTenantRequiresFamilyDomainEnv(t *testing.T) {
|
||||
t.Setenv("SAMAN_DOMAIN_ID", "")
|
||||
|
||||
rootConfig := domain.JSONMap{
|
||||
"worksmobile": map[string]any{
|
||||
"domainMappings": map[string]any{
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
import { createRequire } from "node:module";
|
||||
import { defineConfig, devices } from "@playwright/test";
|
||||
|
||||
const require = createRequire(import.meta.url);
|
||||
const { shouldIncludeWebKit } =
|
||||
require("../scripts/playwrightHostDeps.cjs") as {
|
||||
shouldIncludeWebKit: () => boolean;
|
||||
};
|
||||
|
||||
const configuredWorkers = process.env.PLAYWRIGHT_WORKERS
|
||||
? Number.parseInt(process.env.PLAYWRIGHT_WORKERS, 10)
|
||||
: undefined;
|
||||
@@ -52,10 +59,14 @@ export default defineConfig({
|
||||
use: { ...devices["Desktop Firefox"] },
|
||||
},
|
||||
|
||||
...(shouldIncludeWebKit()
|
||||
? [
|
||||
{
|
||||
name: "webkit",
|
||||
use: { ...devices["Desktop Safari"] },
|
||||
},
|
||||
]
|
||||
: []),
|
||||
],
|
||||
|
||||
/* Run your local dev server before starting the tests */
|
||||
|
||||
@@ -35,6 +35,29 @@ if [ "${1:-}" = "--print-mode" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
ensure_frontend_dependencies() {
|
||||
if [ ! -f package.json ] || [ ! -f package-lock.json ]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
if command -v sha256sum >/dev/null 2>&1; then
|
||||
deps_hash="$(sha256sum package.json package-lock.json | sha256sum | awk '{print $1}')"
|
||||
else
|
||||
deps_hash="$(cksum package.json package-lock.json | cksum | awk '{print $1}')"
|
||||
fi
|
||||
deps_stamp="node_modules/.baron-deps-hash"
|
||||
installed_hash="$(cat "$deps_stamp" 2>/dev/null || true)"
|
||||
|
||||
if [ "$installed_hash" != "$deps_hash" ]; then
|
||||
echo "Installing frontend dependencies from package-lock.json..."
|
||||
npm ci
|
||||
mkdir -p node_modules
|
||||
printf '%s\n' "$deps_hash" > "$deps_stamp"
|
||||
fi
|
||||
}
|
||||
|
||||
ensure_frontend_dependencies
|
||||
|
||||
if [ "$mode" = "production" ]; then
|
||||
echo "Running in production mode with Vite preview..."
|
||||
exec sh -c "npm run build && npm run preview -- --host 0.0.0.0"
|
||||
|
||||
@@ -208,6 +208,11 @@ subtitle = "Review registered tenants and manage their current status."
|
||||
[msg.admin.tenants.import_preview]
|
||||
description = "Rows without tenant_id are compared with existing tenant candidates, then imported as new tenants or updates."
|
||||
|
||||
[msg.admin.tenants.parent]
|
||||
local_picker_description = "Select the tenant to use as the parent from the tenant list."
|
||||
local_picker_empty = "No selectable tenants are available."
|
||||
picker_description = "Select a tenant in org-chart to apply it as the parent tenant."
|
||||
|
||||
[msg.admin.tenants.admins]
|
||||
add_success = "Tenant admin added successfully."
|
||||
empty = "No tenant admins are assigned yet."
|
||||
@@ -218,6 +223,7 @@ remove_success = "Tenant admin removed successfully."
|
||||
subtitle = "Manage the administrators assigned to this tenant."
|
||||
|
||||
[msg.admin.tenants.create]
|
||||
pick_parent_first = "Select the parent tenant first."
|
||||
subtitle = "Enter the minimum required information to create a tenant."
|
||||
|
||||
[msg.admin.tenants.create.form]
|
||||
@@ -1124,9 +1130,14 @@ title = "Domain conflict"
|
||||
candidates = "Candidates"
|
||||
confirm = "Run import"
|
||||
create_new_reset = "Create new (reset ID/slug)"
|
||||
csv_parents = "CSV Parents"
|
||||
external_id = "External ID"
|
||||
match = "Match"
|
||||
no_candidates = "No candidates"
|
||||
parent = "Parent"
|
||||
parent_companies = "Parent Companies"
|
||||
parent_company_groups = "Parent Company Groups"
|
||||
parent_organizations = "Parent Organizations"
|
||||
parent_unresolved = "Parent needs review"
|
||||
slug_exists = "slug conflict"
|
||||
title = "Confirm CSV import"
|
||||
@@ -1163,11 +1174,20 @@ domains_placeholder = "example.com, example.kr"
|
||||
name = "Tenant name"
|
||||
name_placeholder = "Enter tenant name"
|
||||
parent = "Parent"
|
||||
pick_hanmac_parent = "Pick from Hanmac Family"
|
||||
pick_other_parent = "Pick another tenant"
|
||||
root_tenant = "Create as top-level tenant"
|
||||
slug = "Slug"
|
||||
slug_placeholder = "tenant-slug"
|
||||
status = "Status"
|
||||
type = "Type"
|
||||
|
||||
[ui.admin.tenants.create.parent_context]
|
||||
general = "General child tenant"
|
||||
hanmac = "Hanmac Family child tenant"
|
||||
pick_required = "Parent tenant selection required"
|
||||
root = "Top-level tenant"
|
||||
|
||||
[ui.admin.tenants.create.memo]
|
||||
title = "Policy Memo"
|
||||
|
||||
@@ -1249,9 +1269,14 @@ view_profile = "View Profile"
|
||||
candidates = "Candidates"
|
||||
confirm = "Confirm Import"
|
||||
create_new = "Create New"
|
||||
csv_parents = "CSV Parents"
|
||||
fixed_id = "Fixed ID"
|
||||
match = "Matched Tenant"
|
||||
no_candidates = "No matching tenants found."
|
||||
parent = "Parent"
|
||||
parent_companies = "Parent Companies"
|
||||
parent_company_groups = "Parent Company Groups"
|
||||
parent_organizations = "Parent Organizations"
|
||||
title = "Import Preview"
|
||||
|
||||
[ui.admin.tenants.members.table]
|
||||
@@ -1278,16 +1303,22 @@ allowed_domains_help = "Users with these email domains will be automatically ass
|
||||
approve_button = "Approve Tenant"
|
||||
description = "Review and edit the tenant's basic profile information."
|
||||
name = "Tenant Name"
|
||||
org_unit_type = "Organization detail type"
|
||||
slug = "Slug"
|
||||
status = "Status"
|
||||
subtitle = "Slug and status changes are applied immediately."
|
||||
title = "Tenant Profile"
|
||||
type = "Type"
|
||||
visibility = "Visibility"
|
||||
|
||||
[ui.admin.tenants.profile.form]
|
||||
parent = "Parent Tenant (Optional)"
|
||||
parent_help = "Select a parent tenant if this is a subsidiary or sub-organization."
|
||||
|
||||
[ui.admin.tenants.parent]
|
||||
local_search_placeholder = "Search tenant name or slug"
|
||||
pick_tenant = "Pick tenant"
|
||||
|
||||
[ui.admin.tenants.registry]
|
||||
title = "Tenant registry"
|
||||
|
||||
|
||||
@@ -120,6 +120,11 @@ subtitle = "현재 등록된 테넌트를 확인하고 상태를 관리합니다
|
||||
[msg.admin.tenants.import_preview]
|
||||
description = "tenant_id가 없는 행은 기존 테넌트 후보와 비교한 뒤 신규 생성 또는 기존 테넌트 갱신으로 처리합니다."
|
||||
|
||||
[msg.admin.tenants.parent]
|
||||
local_picker_description = "테넌트 목록에서 상위 테넌트로 사용할 항목을 선택합니다."
|
||||
local_picker_empty = "선택할 수 있는 테넌트가 없습니다."
|
||||
picker_description = "org-chart에서 테넌트를 선택하면 상위 테넌트에 반영됩니다."
|
||||
|
||||
[msg.dev.auth]
|
||||
access_denied_description = "DevFront는 관리자 전용 화면입니다. 권한이 필요하면 관리자에게 요청해 주세요."
|
||||
access_denied_title = "접근 권한이 없습니다."
|
||||
@@ -368,9 +373,14 @@ title = "도메인 충돌"
|
||||
candidates = "후보"
|
||||
confirm = "가져오기 실행"
|
||||
create_new_reset = "신규 생성 (ID/slug 재설정)"
|
||||
csv_parents = "CSV 상위 테넌트"
|
||||
external_id = "외부 ID"
|
||||
match = "매칭"
|
||||
no_candidates = "후보 없음"
|
||||
parent = "상위"
|
||||
parent_companies = "상위 회사"
|
||||
parent_company_groups = "상위 그룹사"
|
||||
parent_organizations = "상위 조직"
|
||||
parent_unresolved = "부모 확인 필요"
|
||||
slug_exists = "slug 충돌"
|
||||
title = "CSV 가져오기 확인"
|
||||
@@ -699,6 +709,7 @@ remove_success = "권한이 회수되었습니다."
|
||||
subtitle = "이 테넌트의 자원을 관리할 수 있는 사용자 목록입니다."
|
||||
|
||||
[msg.admin.tenants.create]
|
||||
pick_parent_first = "상위 테넌트를 먼저 선택하세요."
|
||||
subtitle = "글로벌 운영 기준의 신규 테넌트를 등록합니다."
|
||||
|
||||
[msg.admin.tenants.create.form]
|
||||
@@ -1623,11 +1634,20 @@ domains_placeholder = "example.com, example.kr"
|
||||
name = "테넌트 이름"
|
||||
name_placeholder = "테넌트 이름을 입력하세요"
|
||||
parent = "상위 테넌트"
|
||||
pick_hanmac_parent = "한맥가족에서 선택"
|
||||
pick_other_parent = "다른 테넌트 선택"
|
||||
root_tenant = "최상위 테넌트로 생성"
|
||||
slug = "Slug"
|
||||
slug_placeholder = "tenant-slug"
|
||||
status = "상태"
|
||||
type = "유형"
|
||||
|
||||
[ui.admin.tenants.create.parent_context]
|
||||
general = "일반 하위 테넌트"
|
||||
hanmac = "한맥가족 하위 테넌트"
|
||||
pick_required = "상위 테넌트 선택 필요"
|
||||
root = "최상위 테넌트"
|
||||
|
||||
[ui.admin.tenants.create.memo]
|
||||
title = "정책 메모"
|
||||
|
||||
@@ -1711,9 +1731,14 @@ view_profile = "상세 정보"
|
||||
candidates = "후보"
|
||||
confirm = "임포트 확정"
|
||||
create_new = "새로 생성"
|
||||
csv_parents = "CSV 상위 테넌트"
|
||||
fixed_id = "고정 ID"
|
||||
match = "매칭된 테넌트"
|
||||
no_candidates = "매칭 가능한 테넌트가 없습니다."
|
||||
parent = "상위"
|
||||
parent_companies = "상위 회사"
|
||||
parent_company_groups = "상위 그룹사"
|
||||
parent_organizations = "상위 조직"
|
||||
title = "임포트 미리보기"
|
||||
|
||||
[ui.admin.tenants.members.table]
|
||||
@@ -1740,16 +1765,22 @@ allowed_domains_help = "이 도메인을 가진 이메일로 가입한 사용자
|
||||
approve_button = "테넌트 승인"
|
||||
description = "설명"
|
||||
name = "테넌트 이름"
|
||||
org_unit_type = "조직 세부타입"
|
||||
slug = "슬러그 (Slug)"
|
||||
status = "상태"
|
||||
subtitle = "슬러그 및 상태 변경은 즉시 적용됩니다."
|
||||
title = "테넌트 프로필"
|
||||
type = "테넌트 유형"
|
||||
visibility = "공개 범위"
|
||||
|
||||
[ui.admin.tenants.profile.form]
|
||||
parent = "상위 테넌트 (선택)"
|
||||
parent_help = "가족사 테넌트나 하위 조직을 종속시킬 경우 상위 테넌트를 선택해주세요."
|
||||
|
||||
[ui.admin.tenants.parent]
|
||||
local_search_placeholder = "테넌트 이름 또는 슬러그 검색"
|
||||
pick_tenant = "테넌트 선택"
|
||||
|
||||
[ui.admin.tenants.registry]
|
||||
title = "Tenant registry"
|
||||
|
||||
|
||||
@@ -237,9 +237,14 @@ title = ""
|
||||
candidates = ""
|
||||
confirm = ""
|
||||
create_new_reset = ""
|
||||
csv_parents = ""
|
||||
external_id = ""
|
||||
match = ""
|
||||
no_candidates = ""
|
||||
parent = ""
|
||||
parent_companies = ""
|
||||
parent_company_groups = ""
|
||||
parent_organizations = ""
|
||||
parent_unresolved = ""
|
||||
slug_exists = ""
|
||||
title = ""
|
||||
@@ -568,6 +573,7 @@ remove_success = ""
|
||||
subtitle = ""
|
||||
|
||||
[msg.admin.tenants.create]
|
||||
pick_parent_first = ""
|
||||
subtitle = ""
|
||||
|
||||
[msg.admin.tenants.create.form]
|
||||
@@ -1492,11 +1498,20 @@ domains_placeholder = ""
|
||||
name = ""
|
||||
name_placeholder = ""
|
||||
parent = ""
|
||||
pick_hanmac_parent = ""
|
||||
pick_other_parent = ""
|
||||
root_tenant = ""
|
||||
slug = ""
|
||||
slug_placeholder = ""
|
||||
status = ""
|
||||
type = ""
|
||||
|
||||
[ui.admin.tenants.create.parent_context]
|
||||
general = ""
|
||||
hanmac = ""
|
||||
pick_required = ""
|
||||
root = ""
|
||||
|
||||
[ui.admin.tenants.create.memo]
|
||||
title = ""
|
||||
|
||||
@@ -1565,6 +1580,11 @@ seed_delete_blocked = ""
|
||||
[msg.admin.tenants.import_preview]
|
||||
description = ""
|
||||
|
||||
[msg.admin.tenants.parent]
|
||||
local_picker_description = ""
|
||||
local_picker_empty = ""
|
||||
picker_description = ""
|
||||
|
||||
[msg.admin.users]
|
||||
self_delete_blocked = ""
|
||||
export_error = ""
|
||||
@@ -1610,16 +1630,22 @@ allowed_domains_help = ""
|
||||
approve_button = ""
|
||||
description = ""
|
||||
name = ""
|
||||
org_unit_type = ""
|
||||
slug = ""
|
||||
status = ""
|
||||
subtitle = ""
|
||||
title = ""
|
||||
type = ""
|
||||
visibility = ""
|
||||
|
||||
[ui.admin.tenants.profile.form]
|
||||
parent = ""
|
||||
parent_help = ""
|
||||
|
||||
[ui.admin.tenants.parent]
|
||||
local_search_placeholder = ""
|
||||
pick_tenant = ""
|
||||
|
||||
[ui.admin.tenants.registry]
|
||||
title = ""
|
||||
|
||||
@@ -1662,9 +1688,14 @@ tree_search_placeholder = ""
|
||||
candidates = ""
|
||||
confirm = ""
|
||||
create_new = ""
|
||||
csv_parents = ""
|
||||
fixed_id = ""
|
||||
match = ""
|
||||
no_candidates = ""
|
||||
parent = ""
|
||||
parent_companies = ""
|
||||
parent_company_groups = ""
|
||||
parent_organizations = ""
|
||||
title = ""
|
||||
|
||||
[ui.admin.tenants.sub.table]
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
import { createRequire } from "node:module";
|
||||
import { defineConfig, devices } from "@playwright/test";
|
||||
|
||||
const require = createRequire(import.meta.url);
|
||||
const { shouldIncludeWebKit } =
|
||||
require("../scripts/playwrightHostDeps.cjs") as {
|
||||
shouldIncludeWebKit: () => boolean;
|
||||
};
|
||||
|
||||
const configuredWorkers = process.env.PLAYWRIGHT_WORKERS
|
||||
? Number.parseInt(process.env.PLAYWRIGHT_WORKERS, 10)
|
||||
: undefined;
|
||||
@@ -58,10 +65,14 @@ export default defineConfig({
|
||||
use: { ...devices["Desktop Firefox"] },
|
||||
},
|
||||
|
||||
...(shouldIncludeWebKit()
|
||||
? [
|
||||
{
|
||||
name: "webkit",
|
||||
use: { ...devices["Desktop Safari"] },
|
||||
},
|
||||
]
|
||||
: []),
|
||||
],
|
||||
|
||||
/* Run your local dev server before starting the tests */
|
||||
|
||||
@@ -35,6 +35,29 @@ if [ "${1:-}" = "--print-mode" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
ensure_frontend_dependencies() {
|
||||
if [ ! -f package.json ] || [ ! -f package-lock.json ]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
if command -v sha256sum >/dev/null 2>&1; then
|
||||
deps_hash="$(sha256sum package.json package-lock.json | sha256sum | awk '{print $1}')"
|
||||
else
|
||||
deps_hash="$(cksum package.json package-lock.json | cksum | awk '{print $1}')"
|
||||
fi
|
||||
deps_stamp="node_modules/.baron-deps-hash"
|
||||
installed_hash="$(cat "$deps_stamp" 2>/dev/null || true)"
|
||||
|
||||
if [ "$installed_hash" != "$deps_hash" ]; then
|
||||
echo "Installing frontend dependencies from package-lock.json..."
|
||||
npm ci
|
||||
mkdir -p node_modules
|
||||
printf '%s\n' "$deps_hash" > "$deps_stamp"
|
||||
fi
|
||||
}
|
||||
|
||||
ensure_frontend_dependencies
|
||||
|
||||
if [ "$mode" = "production" ]; then
|
||||
echo "Running in production mode with Vite preview..."
|
||||
exec sh -c "npm run build && npm run preview -- --host 0.0.0.0 --port 5175"
|
||||
|
||||
@@ -46,9 +46,7 @@ describe("hanmac family organization order", () => {
|
||||
|
||||
it("does not rank generic technical centers as GPDTDC", () => {
|
||||
expect(
|
||||
getHanmacFamilyTenantOrderRank(
|
||||
tenant("기술개발센터", "rnd-center"),
|
||||
),
|
||||
getHanmacFamilyTenantOrderRank(tenant("기술개발센터", "rnd-center")),
|
||||
).toBe(Number.MAX_SAFE_INTEGER);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -51,10 +51,9 @@ function tenantToPickerNode(
|
||||
tenant: TenantNode,
|
||||
usersBySlug: Map<string, UserSummary[]>,
|
||||
): OrgPickerTreeNode {
|
||||
const tenantChildren = orderHanmacFamilyChildren(
|
||||
tenant,
|
||||
tenant.children,
|
||||
).map((child) => tenantToPickerNode(child, usersBySlug));
|
||||
const tenantChildren = orderHanmacFamilyChildren(tenant, tenant.children).map(
|
||||
(child) => tenantToPickerNode(child, usersBySlug),
|
||||
);
|
||||
const userChildren = (usersBySlug.get(tenant.slug.toLowerCase()) || []).map(
|
||||
(user) => ({
|
||||
type: "user" as const,
|
||||
|
||||
@@ -1032,8 +1032,7 @@ export function buildOrgSelectionOptions(
|
||||
(familyRoot?.children ?? []).filter((node) =>
|
||||
["COMPANY_GROUP", "COMPANY", "ORGANIZATION"].includes(node.type),
|
||||
),
|
||||
)
|
||||
.map((node) => ({
|
||||
).map((node) => ({
|
||||
descendants: collectOrgSelectionDescendants(node, 2),
|
||||
id: node.id,
|
||||
label: node.name,
|
||||
|
||||
60
scripts/playwrightHostDeps.cjs
Normal file
60
scripts/playwrightHostDeps.cjs
Normal file
@@ -0,0 +1,60 @@
|
||||
const { execFileSync } = require("node:child_process");
|
||||
|
||||
const webkitHostLibraries = [
|
||||
"libgtk-4.so.1",
|
||||
"libgraphene-1.0.so.0",
|
||||
"libxslt.so.1",
|
||||
"libevent-2.1.so.7",
|
||||
"libopus.so.0",
|
||||
"libgstallocators-1.0.so.0",
|
||||
"libgstapp-1.0.so.0",
|
||||
"libgstpbutils-1.0.so.0",
|
||||
"libgstaudio-1.0.so.0",
|
||||
"libgsttag-1.0.so.0",
|
||||
"libgstvideo-1.0.so.0",
|
||||
"libgstgl-1.0.so.0",
|
||||
"libgstcodecparsers-1.0.so.0",
|
||||
"libgstfft-1.0.so.0",
|
||||
"libflite.so.1",
|
||||
"libwebpdemux.so.2",
|
||||
"libavif.so.16",
|
||||
"libharfbuzz-icu.so.0",
|
||||
"libwebpmux.so.3",
|
||||
"libwayland-server.so.0",
|
||||
"libmanette-0.2.so.0",
|
||||
"libenchant-2.so.2",
|
||||
"libhyphen.so.0",
|
||||
"libsecret-1.so.0",
|
||||
"libwoff2dec.so.1.0.2",
|
||||
"libx264.so",
|
||||
];
|
||||
|
||||
function hasWebKitHostDependencies() {
|
||||
if (process.platform !== "linux") {
|
||||
return true;
|
||||
}
|
||||
|
||||
let output = "";
|
||||
try {
|
||||
output = execFileSync("ldconfig", ["-p"], { encoding: "utf8" });
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
|
||||
return webkitHostLibraries.every((library) => output.includes(library));
|
||||
}
|
||||
|
||||
function shouldIncludeWebKit() {
|
||||
if (process.env.PLAYWRIGHT_FORCE_WEBKIT === "1") {
|
||||
return true;
|
||||
}
|
||||
if (process.env.PLAYWRIGHT_SKIP_WEBKIT === "1") {
|
||||
return false;
|
||||
}
|
||||
return hasWebKitHostDependencies();
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
hasWebKitHostDependencies,
|
||||
shouldIncludeWebKit,
|
||||
};
|
||||
@@ -18,6 +18,8 @@ rm -rf adminfront/node_modules
|
||||
|
||||
tmp_dir="$(mktemp -d /tmp/baron-sso-adminfront-tests.XXXXXX)"
|
||||
playwright_browsers_path="$tmp_dir/ms-playwright"
|
||||
mkdir -p "$tmp_dir/scripts"
|
||||
cp "$repo_root/scripts/playwrightHostDeps.cjs" "$tmp_dir/scripts/"
|
||||
|
||||
if command -v rsync >/dev/null 2>&1; then
|
||||
rsync -rlptD --delete \
|
||||
@@ -58,6 +60,53 @@ find_available_port() {
|
||||
|
||||
playwright_install_cmd=(npx playwright install)
|
||||
playwright_install_desc="npx playwright install"
|
||||
playwright_project_args=()
|
||||
|
||||
has_webkit_host_dependencies() {
|
||||
if [ "$(uname -s)" != "Linux" ]; then
|
||||
return 0
|
||||
fi
|
||||
if ! command -v ldconfig >/dev/null 2>&1; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
local missing=0
|
||||
local lib
|
||||
for lib in \
|
||||
libgtk-4.so.1 \
|
||||
libgraphene-1.0.so.0 \
|
||||
libxslt.so.1 \
|
||||
libevent-2.1.so.7 \
|
||||
libopus.so.0 \
|
||||
libgstallocators-1.0.so.0 \
|
||||
libgstapp-1.0.so.0 \
|
||||
libgstpbutils-1.0.so.0 \
|
||||
libgstaudio-1.0.so.0 \
|
||||
libgsttag-1.0.so.0 \
|
||||
libgstvideo-1.0.so.0 \
|
||||
libgstgl-1.0.so.0 \
|
||||
libgstcodecparsers-1.0.so.0 \
|
||||
libgstfft-1.0.so.0 \
|
||||
libflite.so.1 \
|
||||
libwebpdemux.so.2 \
|
||||
libavif.so.16 \
|
||||
libharfbuzz-icu.so.0 \
|
||||
libwebpmux.so.3 \
|
||||
libwayland-server.so.0 \
|
||||
libmanette-0.2.so.0 \
|
||||
libenchant-2.so.2 \
|
||||
libhyphen.so.0 \
|
||||
libsecret-1.so.0 \
|
||||
libwoff2dec.so.1.0.2 \
|
||||
libx264.so; do
|
||||
if ! ldconfig -p 2>/dev/null | grep -Fq "$lib"; then
|
||||
missing=1
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
[ "$missing" -eq 0 ]
|
||||
}
|
||||
|
||||
if [ "$(id -u)" -eq 0 ]; then
|
||||
playwright_install_cmd=(npx playwright install --with-deps)
|
||||
@@ -65,6 +114,17 @@ if [ "$(id -u)" -eq 0 ]; then
|
||||
elif command -v sudo >/dev/null 2>&1 && sudo -n true >/dev/null 2>&1; then
|
||||
playwright_install_cmd=(npx playwright install --with-deps)
|
||||
playwright_install_desc="npx playwright install --with-deps"
|
||||
elif ! has_webkit_host_dependencies; then
|
||||
playwright_install_cmd=(npx playwright install chromium firefox)
|
||||
playwright_install_desc="npx playwright install chromium firefox"
|
||||
playwright_project_args=(--project=chromium --project=firefox)
|
||||
{
|
||||
echo "# Adminfront WebKit Skipped"
|
||||
echo
|
||||
echo "- Reason: WebKit host dependencies are not installed and this user cannot run passwordless sudo."
|
||||
echo "- Action: Running Chromium and Firefox projects only."
|
||||
echo "- To enable WebKit locally: run \`cd adminfront && npx playwright install-deps webkit\` with sudo privileges."
|
||||
} > reports/adminfront-webkit-skipped.md
|
||||
fi
|
||||
|
||||
set +e
|
||||
@@ -134,7 +194,7 @@ echo "==> adminfront using PORT=$port"
|
||||
(
|
||||
cd "$tmp_dir/adminfront"
|
||||
PORT="$port" PLAYWRIGHT_WORKERS="${PLAYWRIGHT_WORKERS:-1}" PLAYWRIGHT_BROWSERS_PATH="$playwright_browsers_path" \
|
||||
node ./node_modules/playwright/cli.js test
|
||||
node ./node_modules/playwright/cli.js test "${playwright_project_args[@]}"
|
||||
) 2>&1 | tee reports/adminfront-test.log
|
||||
test_exit_code=${PIPESTATUS[0]}
|
||||
set -e
|
||||
|
||||
@@ -17,6 +17,14 @@ for script in \
|
||||
"./devfront/scripts/runtime-mode.sh" \
|
||||
"./orgfront/scripts/runtime-mode.sh"
|
||||
do
|
||||
if ! grep -Fq "ensure_frontend_dependencies" "$script"; then
|
||||
echo "script=$script must sync frontend dependencies before start" >&2
|
||||
exit 1
|
||||
fi
|
||||
if ! grep -Fq "package-lock.json" "$script"; then
|
||||
echo "script=$script must use package-lock.json for dependency sync" >&2
|
||||
exit 1
|
||||
fi
|
||||
assert_mode "$script" "production" "production"
|
||||
assert_mode "$script" "prod" "production"
|
||||
assert_mode "$script" "stage" "production"
|
||||
|
||||
@@ -24,13 +24,19 @@ pull_compose="docker/staging_pull_compose.template.yaml"
|
||||
devfront_vite="devfront/vite.config.ts"
|
||||
orgfront_vite="orgfront/vite.config.ts"
|
||||
adminfront_vite="adminfront/vite.config.ts"
|
||||
adminfront_runtime="adminfront/scripts/runtime-mode.sh"
|
||||
devfront_runtime="devfront/scripts/runtime-mode.sh"
|
||||
orgfront_runtime="orgfront/scripts/runtime-mode.sh"
|
||||
|
||||
for file in \
|
||||
"$staging_pull" \
|
||||
"$pull_compose" \
|
||||
"$adminfront_vite" \
|
||||
"$devfront_vite" \
|
||||
"$orgfront_vite"
|
||||
"$orgfront_vite" \
|
||||
"$adminfront_runtime" \
|
||||
"$devfront_runtime" \
|
||||
"$orgfront_runtime"
|
||||
do
|
||||
if [ ! -f "$file" ]; then
|
||||
echo "missing expected file: $file" >&2
|
||||
@@ -49,6 +55,7 @@ done
|
||||
assert_contains "$staging_pull" 'bash scripts/render_ory_config.sh'
|
||||
assert_contains "$staging_pull" 'chmod -R 777 config/.generated/ory'
|
||||
assert_contains "$staging_pull" 'docker compose -f staging_pull_compose.yaml build --pull'
|
||||
assert_contains "$staging_pull" 'docker compose -f staging_pull_compose.yaml up -d --remove-orphans --renew-anon-volumes'
|
||||
|
||||
assert_contains "$pull_compose" "baron_devfront"
|
||||
assert_contains "$pull_compose" "baron_orgfront"
|
||||
@@ -71,4 +78,11 @@ assert_contains "$orgfront_vite" '"sorg.hmac.kr"'
|
||||
assert_contains "orgfront/biome.json" '".vite"'
|
||||
assert_contains "orgfront/tsconfig.app.json" '"exclude": ["src/**/*.test.ts", "src/**/*.test.tsx"]'
|
||||
|
||||
for runtime_script in "$adminfront_runtime" "$devfront_runtime" "$orgfront_runtime"; do
|
||||
assert_contains "$runtime_script" "ensure_frontend_dependencies"
|
||||
assert_contains "$runtime_script" "package-lock.json"
|
||||
assert_contains "$runtime_script" "npm ci"
|
||||
assert_contains "$runtime_script" ".baron-deps-hash"
|
||||
done
|
||||
|
||||
echo "staging frontend deploy policy checks passed"
|
||||
|
||||
@@ -45,10 +45,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: characters
|
||||
sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803
|
||||
sha256: faf38497bda5ead2a8c7615f4f7939df04333478bf32e4173fcb06d428b5716b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.0"
|
||||
version: "1.4.1"
|
||||
cli_config:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -276,14 +276,6 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.5"
|
||||
js:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: js
|
||||
sha256: "53385261521cc4a0c4658fd0ad07a7d14591cf8fc33abbceae306ddb974888dc"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.7.2"
|
||||
leak_tracker:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -336,18 +328,18 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: matcher
|
||||
sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2
|
||||
sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.12.17"
|
||||
version: "0.12.19"
|
||||
material_color_utilities:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: material_color_utilities
|
||||
sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
|
||||
sha256: "9c337007e82b1889149c82ed242ed1cb24a66044e30979c44912381e9be4c48b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.11.1"
|
||||
version: "0.13.0"
|
||||
meta:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -669,26 +661,26 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test
|
||||
sha256: "75906bf273541b676716d1ca7627a17e4c4070a3a16272b7a3dc7da3b9f3f6b7"
|
||||
sha256: "280d6d890011ca966ad08df7e8a4ddfab0fb3aa49f96ed6de56e3521347a9ae7"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.26.3"
|
||||
version: "1.30.0"
|
||||
test_api:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_api
|
||||
sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55
|
||||
sha256: "8161c84903fd860b26bfdefb7963b3f0b68fee7adea0f59ef805ecca346f0c7a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.7.7"
|
||||
version: "0.7.10"
|
||||
test_core:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_core
|
||||
sha256: "0cc24b5ff94b38d2ae73e1eb43cc302b77964fbf67abad1e296025b78deb53d0"
|
||||
sha256: "0381bd1585d1a924763c308100f2138205252fb90c9d4eeaf28489ee65ccde51"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.6.12"
|
||||
version: "0.6.16"
|
||||
toml:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
||||
Reference in New Issue
Block a user