forked from baron/baron-sso
로컬/CI 테스트 동기화
This commit is contained in:
@@ -95,11 +95,12 @@ jobs:
|
|||||||
npx biome check . --linter-enabled=false --organize-imports-enabled=false
|
npx biome check . --linter-enabled=false --organize-imports-enabled=false
|
||||||
|
|
||||||
- name: Lint Go backend
|
- name: Lint Go backend
|
||||||
uses: golangci/golangci-lint-action@v6
|
run: |
|
||||||
with:
|
docker run --rm \
|
||||||
version: v1.59
|
-v "${PWD}/backend:/app" \
|
||||||
working-directory: backend
|
-w /app \
|
||||||
args: --enable-only=gofmt,gofumpt
|
golangci/golangci-lint:v2.10.1 \
|
||||||
|
golangci-lint fmt -E gofmt -E gofumpt -d
|
||||||
|
|
||||||
- name: Sync userfront locales
|
- name: Sync userfront locales
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
143
Makefile
143
Makefile
@@ -107,11 +107,30 @@ logs-app:
|
|||||||
docker compose -f $(COMPOSE_APP) logs -f
|
docker compose -f $(COMPOSE_APP) logs -f
|
||||||
|
|
||||||
# --- 로컬 통합 코드 체크 ---
|
# --- 로컬 통합 코드 체크 ---
|
||||||
.PHONY: code-check code-check-i18n code-check-go-lint code-check-userfront-lint code-check-front-lint code-check-backend-tests code-check-userfront-tests code-check-adminfront-tests code-check-devfront-tests code-check-userfront-e2e-tests
|
ifeq ($(CI),)
|
||||||
|
PLAYWRIGHT_INSTALL_ALL := npx playwright install
|
||||||
|
PLAYWRIGHT_INSTALL_CHROMIUM := npx playwright install chromium
|
||||||
|
else
|
||||||
|
PLAYWRIGHT_INSTALL_ALL := npx playwright install --with-deps
|
||||||
|
PLAYWRIGHT_INSTALL_CHROMIUM := npx playwright install --with-deps chromium
|
||||||
|
endif
|
||||||
|
|
||||||
code-check: code-check-i18n code-check-go-lint code-check-userfront-lint code-check-front-lint code-check-backend-tests code-check-userfront-tests code-check-adminfront-tests code-check-devfront-tests code-check-userfront-e2e-tests
|
.PHONY: code-check code-check-lint code-check-test-jobs code-check-i18n code-check-go-lint code-check-sync-userfront-locales code-check-userfront-install code-check-userfront-lint code-check-front-lint code-check-backend-tests code-check-userfront-tests code-check-adminfront-tests code-check-devfront-tests code-check-userfront-e2e-tests
|
||||||
|
|
||||||
|
code-check: code-check-lint code-check-test-jobs
|
||||||
@echo "code-check complete."
|
@echo "code-check complete."
|
||||||
|
|
||||||
|
code-check-lint: code-check-i18n code-check-front-lint code-check-go-lint code-check-sync-userfront-locales code-check-userfront-install code-check-userfront-lint
|
||||||
|
|
||||||
|
code-check-test-jobs:
|
||||||
|
@echo "==> run CI-equivalent test jobs (parallel)"
|
||||||
|
@$(MAKE) --no-print-directory -j5 --output-sync=target \
|
||||||
|
code-check-backend-tests \
|
||||||
|
code-check-userfront-tests \
|
||||||
|
code-check-userfront-e2e-tests \
|
||||||
|
code-check-adminfront-tests \
|
||||||
|
code-check-devfront-tests
|
||||||
|
|
||||||
code-check-i18n:
|
code-check-i18n:
|
||||||
@echo "==> i18n resource check"
|
@echo "==> i18n resource check"
|
||||||
@mkdir -p reports
|
@mkdir -p reports
|
||||||
@@ -122,60 +141,83 @@ code-check-i18n:
|
|||||||
code-check-go-lint:
|
code-check-go-lint:
|
||||||
@echo "==> go lint/format check"
|
@echo "==> go lint/format check"
|
||||||
@if command -v golangci-lint >/dev/null 2>&1; then \
|
@if command -v golangci-lint >/dev/null 2>&1; then \
|
||||||
cd backend && golangci-lint run --enable-only=gofmt,gofumpt; \
|
cd backend && golangci-lint fmt -E gofmt -E gofumpt -d; \
|
||||||
|
elif command -v docker >/dev/null 2>&1; then \
|
||||||
|
docker run --rm \
|
||||||
|
-v "$$(pwd)/backend:/app" \
|
||||||
|
-w /app \
|
||||||
|
golangci/golangci-lint:v2.10.1 \
|
||||||
|
golangci-lint fmt -E gofmt -E gofumpt -d; \
|
||||||
else \
|
else \
|
||||||
echo "WARN: golangci-lint not found, fallback to gofmt check only."; \
|
echo "ERROR: golangci-lint not found and docker is unavailable."; \
|
||||||
unformatted="$$(cd backend && gofmt -l .)"; \
|
echo "Install golangci-lint v2.10.1 or Docker to match CI lint step."; \
|
||||||
if [ -n "$$unformatted" ]; then \
|
exit 1; \
|
||||||
echo "gofmt required:"; \
|
|
||||||
echo "$$unformatted"; \
|
|
||||||
exit 1; \
|
|
||||||
fi; \
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
code-check-sync-userfront-locales:
|
||||||
|
@echo "==> sync userfront locales"
|
||||||
|
/bin/sh ./scripts/sync_userfront_locales.sh
|
||||||
|
|
||||||
|
code-check-userfront-install:
|
||||||
|
@echo "==> install userfront dependencies"
|
||||||
|
cd userfront && flutter pub get
|
||||||
|
|
||||||
code-check-userfront-lint:
|
code-check-userfront-lint:
|
||||||
@echo "==> userfront format/analyze"
|
@echo "==> userfront format/analyze"
|
||||||
cd userfront && flutter pub get
|
cd userfront && dart format --output=none --set-exit-if-changed lib test
|
||||||
cd userfront && dart format --output=show --set-exit-if-changed lib test
|
|
||||||
cd userfront && flutter analyze --no-fatal-warnings --no-fatal-infos
|
cd userfront && flutter analyze --no-fatal-warnings --no-fatal-infos
|
||||||
|
|
||||||
code-check-front-lint:
|
code-check-front-lint:
|
||||||
@echo "==> adminfront biome lint/format check"
|
@echo "==> adminfront biome lint/format check"
|
||||||
|
rm -rf adminfront/playwright-report adminfront/test-results
|
||||||
cd adminfront && npm ci
|
cd adminfront && npm ci
|
||||||
cd adminfront && npx biome check src tests playwright.config.ts --formatter-enabled=false --organize-imports-enabled=false
|
cd adminfront && npx biome check . --formatter-enabled=false --organize-imports-enabled=false
|
||||||
cd adminfront && npx biome check src tests playwright.config.ts --linter-enabled=false --organize-imports-enabled=false
|
cd adminfront && npx biome check . --linter-enabled=false --organize-imports-enabled=false
|
||||||
@echo "==> devfront biome lint/format check"
|
@echo "==> devfront biome lint/format check"
|
||||||
|
rm -rf devfront/playwright-report devfront/test-results
|
||||||
cd devfront && npm ci
|
cd devfront && npm ci
|
||||||
cd devfront && npx biome check src tests playwright.config.ts --formatter-enabled=false --organize-imports-enabled=false
|
cd devfront && npx biome check . --formatter-enabled=false --organize-imports-enabled=false
|
||||||
cd devfront && npx biome check src tests playwright.config.ts --linter-enabled=false --organize-imports-enabled=false
|
cd devfront && npx biome check . --linter-enabled=false --organize-imports-enabled=false
|
||||||
|
|
||||||
code-check-backend-tests:
|
code-check-backend-tests:
|
||||||
@echo "==> backend tests"
|
@echo "==> backend tests"
|
||||||
cd backend && go test -v ./...
|
cd backend && go test -v ./...
|
||||||
|
|
||||||
code-check-userfront-tests:
|
code-check-userfront-tests:
|
||||||
@echo "==> userfront tests"
|
@echo "==> userfront tests (isolated workspace)"
|
||||||
cd userfront && flutter test
|
@tmp_dir="$$(mktemp -d /tmp/baron-sso-userfront-tests.XXXXXX)"; \
|
||||||
|
trap 'rm -rf "$$tmp_dir"' EXIT INT TERM; \
|
||||||
|
mkdir -p "$$tmp_dir/scripts"; \
|
||||||
|
cp scripts/sync_userfront_locales.sh "$$tmp_dir/scripts/"; \
|
||||||
|
cp -R locales "$$tmp_dir/locales"; \
|
||||||
|
if command -v rsync >/dev/null 2>&1; then \
|
||||||
|
rsync -a --delete \
|
||||||
|
--exclude '.dart_tool' \
|
||||||
|
--exclude 'build' \
|
||||||
|
--exclude '.pub-cache' \
|
||||||
|
--exclude '.flutter-plugins' \
|
||||||
|
--exclude '.flutter-plugins-dependencies' \
|
||||||
|
userfront/ "$$tmp_dir/userfront/"; \
|
||||||
|
else \
|
||||||
|
cp -R userfront "$$tmp_dir/userfront"; \
|
||||||
|
rm -rf "$$tmp_dir/userfront/.dart_tool" "$$tmp_dir/userfront/build"; \
|
||||||
|
fi; \
|
||||||
|
cd "$$tmp_dir" && /bin/sh ./scripts/sync_userfront_locales.sh; \
|
||||||
|
cd "$$tmp_dir/userfront" && flutter test
|
||||||
|
|
||||||
code-check-adminfront-tests:
|
code-check-adminfront-tests:
|
||||||
@echo "==> adminfront tests"
|
@echo "==> adminfront tests"
|
||||||
@mkdir -p reports/adminfront
|
./scripts/run_adminfront_ci_tests.sh adminfront-tests
|
||||||
@rm -rf reports/adminfront/playwright-report reports/adminfront/test-results
|
|
||||||
@status=0; \
|
|
||||||
(cd adminfront && npx playwright install) || status=$$?; \
|
|
||||||
if [ $$status -eq 0 ]; then \
|
|
||||||
(cd adminfront && npm test) || status=$$?; \
|
|
||||||
fi; \
|
|
||||||
[ -d adminfront/playwright-report ] && cp -R adminfront/playwright-report reports/adminfront/ || true; \
|
|
||||||
[ -d adminfront/test-results ] && cp -R adminfront/test-results reports/adminfront/ || true; \
|
|
||||||
exit $$status
|
|
||||||
|
|
||||||
code-check-devfront-tests:
|
code-check-devfront-tests:
|
||||||
@echo "==> devfront tests"
|
@echo "==> devfront tests"
|
||||||
@mkdir -p reports/devfront
|
@mkdir -p reports/devfront
|
||||||
@rm -rf reports/devfront/playwright-report reports/devfront/test-results
|
@rm -rf reports/devfront/playwright-report reports/devfront/test-results
|
||||||
@status=0; \
|
@status=0; \
|
||||||
(cd devfront && npx playwright install) || status=$$?; \
|
(cd devfront && npm ci) || status=$$?; \
|
||||||
|
if [ $$status -eq 0 ]; then \
|
||||||
|
(cd devfront && $(PLAYWRIGHT_INSTALL_ALL)) || status=$$?; \
|
||||||
|
fi; \
|
||||||
if [ $$status -eq 0 ]; then \
|
if [ $$status -eq 0 ]; then \
|
||||||
(cd devfront && npm test) || status=$$?; \
|
(cd devfront && npm test) || status=$$?; \
|
||||||
fi; \
|
fi; \
|
||||||
@@ -184,20 +226,47 @@ code-check-devfront-tests:
|
|||||||
exit $$status
|
exit $$status
|
||||||
|
|
||||||
code-check-userfront-e2e-tests:
|
code-check-userfront-e2e-tests:
|
||||||
@echo "==> userfront wasm playwright e2e tests"
|
@echo "==> userfront wasm playwright e2e tests (isolated workspace)"
|
||||||
@mkdir -p reports/userfront-e2e
|
@mkdir -p reports/userfront-e2e
|
||||||
@rm -rf reports/userfront-e2e/playwright-report reports/userfront-e2e/test-results
|
@rm -rf reports/userfront-e2e/playwright-report reports/userfront-e2e/test-results
|
||||||
@status=0; \
|
@tmp_dir="$$(mktemp -d /tmp/baron-sso-userfront-e2e-tests.XXXXXX)"; \
|
||||||
(cd userfront && flutter build web --wasm --release) || status=$$?; \
|
trap 'rm -rf "$$tmp_dir"' EXIT INT TERM; \
|
||||||
|
mkdir -p "$$tmp_dir/scripts"; \
|
||||||
|
cp scripts/sync_userfront_locales.sh "$$tmp_dir/scripts/"; \
|
||||||
|
cp -R locales "$$tmp_dir/locales"; \
|
||||||
|
if command -v rsync >/dev/null 2>&1; then \
|
||||||
|
rsync -a --delete \
|
||||||
|
--exclude '.dart_tool' \
|
||||||
|
--exclude 'build' \
|
||||||
|
--exclude '.pub-cache' \
|
||||||
|
--exclude '.flutter-plugins' \
|
||||||
|
--exclude '.flutter-plugins-dependencies' \
|
||||||
|
userfront/ "$$tmp_dir/userfront/"; \
|
||||||
|
rsync -a --delete \
|
||||||
|
--exclude 'node_modules' \
|
||||||
|
--exclude 'playwright-report' \
|
||||||
|
--exclude 'test-results' \
|
||||||
|
userfront-e2e/ "$$tmp_dir/userfront-e2e/"; \
|
||||||
|
else \
|
||||||
|
cp -R userfront "$$tmp_dir/userfront"; \
|
||||||
|
rm -rf "$$tmp_dir/userfront/.dart_tool" "$$tmp_dir/userfront/build"; \
|
||||||
|
cp -R userfront-e2e "$$tmp_dir/userfront-e2e"; \
|
||||||
|
rm -rf "$$tmp_dir/userfront-e2e/node_modules" "$$tmp_dir/userfront-e2e/playwright-report" "$$tmp_dir/userfront-e2e/test-results"; \
|
||||||
|
fi; \
|
||||||
|
status=0; \
|
||||||
|
(cd "$$tmp_dir" && /bin/sh ./scripts/sync_userfront_locales.sh) || status=$$?; \
|
||||||
if [ $$status -eq 0 ]; then \
|
if [ $$status -eq 0 ]; then \
|
||||||
(cd userfront-e2e && npm ci) || status=$$?; \
|
(cd "$$tmp_dir/userfront-e2e" && npm ci) || status=$$?; \
|
||||||
fi; \
|
fi; \
|
||||||
if [ $$status -eq 0 ]; then \
|
if [ $$status -eq 0 ]; then \
|
||||||
(cd userfront-e2e && npx playwright install --with-deps chromium) || status=$$?; \
|
(cd "$$tmp_dir/userfront" && flutter build web --wasm --release) || status=$$?; \
|
||||||
fi; \
|
fi; \
|
||||||
if [ $$status -eq 0 ]; then \
|
if [ $$status -eq 0 ]; then \
|
||||||
(cd userfront-e2e && npm test) || status=$$?; \
|
(cd "$$tmp_dir/userfront-e2e" && $(PLAYWRIGHT_INSTALL_CHROMIUM)) || status=$$?; \
|
||||||
fi; \
|
fi; \
|
||||||
[ -d userfront-e2e/playwright-report ] && cp -R userfront-e2e/playwright-report reports/userfront-e2e/ || true; \
|
if [ $$status -eq 0 ]; then \
|
||||||
[ -d userfront-e2e/test-results ] && cp -R userfront-e2e/test-results reports/userfront-e2e/ || true; \
|
(cd "$$tmp_dir/userfront-e2e" && npm test) || status=$$?; \
|
||||||
|
fi; \
|
||||||
|
[ -d "$$tmp_dir/userfront-e2e/playwright-report" ] && cp -R "$$tmp_dir/userfront-e2e/playwright-report" reports/userfront-e2e/ || true; \
|
||||||
|
[ -d "$$tmp_dir/userfront-e2e/test-results" ] && cp -R "$$tmp_dir/userfront-e2e/test-results" reports/userfront-e2e/ || true; \
|
||||||
exit $$status
|
exit $$status
|
||||||
|
|||||||
@@ -82,4 +82,4 @@ Error generating stack: `+a.message+`
|
|||||||
<div id='root'></div>
|
<div id='root'></div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
<script id="playwrightReportBase64" type="application/zip">data:application/zip;base64,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</script>
|
<script id="playwrightReportBase64" type="application/zip">data:application/zip;base64,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</script>
|
||||||
@@ -66,7 +66,17 @@ cd userfront
|
|||||||
flutter test test/error_screen_test.dart
|
flutter test test/error_screen_test.dart
|
||||||
```
|
```
|
||||||
|
|
||||||
## 6. 관련 이슈
|
## 6. Backend `code` 주입/매핑 경로
|
||||||
|
- 기본 매핑 함수: `backend/internal/response/error_response.go`
|
||||||
|
- `404 -> not_found`
|
||||||
|
- `429 -> rate_limited`
|
||||||
|
- legacy 응답 보강 미들웨어: `backend/internal/middleware/error_code_enricher.go`
|
||||||
|
- 핸들러가 `{"error": ...}`만 반환해도 status 기반 `code`를 주입
|
||||||
|
- 신규 권장 패턴: `response.Error(...)` 또는 공통 helper(`errorJSON`, `errorJSONCode`)로 핸들러에서 명시 코드 반환
|
||||||
|
|
||||||
|
UserFront는 위 경로로 전달된 `code`를 기준으로 whitelist/ory/unknown 분기를 수행합니다.
|
||||||
|
|
||||||
|
## 7. 관련 이슈
|
||||||
- `#164` `[UserFront] 에러 노출 whitelist 정의 및 적용`
|
- `#164` `[UserFront] 에러 노출 whitelist 정의 및 적용`
|
||||||
- `#259` `백엔드 i18n/에러 메시지 fallback 정책 재정리 및 반영 계획 수립`
|
- `#259` `백엔드 i18n/에러 메시지 fallback 정책 재정리 및 반영 계획 수립`
|
||||||
- `#260` `[Backend] 에러 응답 code 통일 구현 계획 (phase rollout)`
|
- `#260` `[Backend] 에러 응답 code 통일 구현 계획 (phase rollout)`
|
||||||
|
|||||||
@@ -5,6 +5,14 @@ job_name="${1:-adminfront-tests}"
|
|||||||
|
|
||||||
mkdir -p reports
|
mkdir -p reports
|
||||||
|
|
||||||
|
if [ -n "${CI:-}" ]; then
|
||||||
|
playwright_install_cmd=(npx playwright install --with-deps)
|
||||||
|
playwright_install_desc="npx playwright install --with-deps"
|
||||||
|
else
|
||||||
|
playwright_install_cmd=(npx playwright install)
|
||||||
|
playwright_install_desc="npx playwright install"
|
||||||
|
fi
|
||||||
|
|
||||||
set +e
|
set +e
|
||||||
(
|
(
|
||||||
cd adminfront
|
cd adminfront
|
||||||
@@ -36,7 +44,7 @@ fi
|
|||||||
set +e
|
set +e
|
||||||
(
|
(
|
||||||
cd adminfront
|
cd adminfront
|
||||||
npx playwright install --with-deps
|
"${playwright_install_cmd[@]}"
|
||||||
) 2>&1 | tee reports/adminfront-provision.log
|
) 2>&1 | tee reports/adminfront-provision.log
|
||||||
provision_exit_code=${PIPESTATUS[0]}
|
provision_exit_code=${PIPESTATUS[0]}
|
||||||
set -e
|
set -e
|
||||||
@@ -51,7 +59,7 @@ if [ "$provision_exit_code" -ne 0 ]; then
|
|||||||
echo "- Exit Code: \`$provision_exit_code\`"
|
echo "- Exit Code: \`$provision_exit_code\`"
|
||||||
echo
|
echo
|
||||||
echo "## Command"
|
echo "## Command"
|
||||||
echo "\`cd adminfront && npx playwright install --with-deps\`"
|
echo "\`cd adminfront && ${playwright_install_desc}\`"
|
||||||
echo
|
echo
|
||||||
echo "## Provision Log Tail (last 200 lines)"
|
echo "## Provision Log Tail (last 200 lines)"
|
||||||
echo '```text'
|
echo '```text'
|
||||||
@@ -80,7 +88,7 @@ if [ "$test_exit_code" -ne 0 ]; then
|
|||||||
echo "## Commands"
|
echo "## Commands"
|
||||||
echo "1. \`cd adminfront\`"
|
echo "1. \`cd adminfront\`"
|
||||||
echo "2. \`npm ci\`"
|
echo "2. \`npm ci\`"
|
||||||
echo "3. \`npx playwright install --with-deps\`"
|
echo "3. \`${playwright_install_desc}\`"
|
||||||
echo "4. \`npm test\`"
|
echo "4. \`npm test\`"
|
||||||
echo
|
echo
|
||||||
echo "## Log Tail (last 200 lines)"
|
echo "## Log Tail (last 200 lines)"
|
||||||
|
|||||||
@@ -75,8 +75,18 @@ async function mockUserfrontApis(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (path.endsWith('/api/v1/auth/qr/approve')) {
|
if (path.endsWith('/api/v1/auth/qr/approve')) {
|
||||||
const body = route.request().postDataJSON() as { pendingRef?: string };
|
if (route.request().method() == 'POST') {
|
||||||
options.captureApprove?.(body.pendingRef ?? null);
|
let pendingRef: string | null = null;
|
||||||
|
try {
|
||||||
|
const body = (route.request().postDataJSON() ?? {}) as {
|
||||||
|
pendingRef?: string;
|
||||||
|
};
|
||||||
|
pendingRef = body.pendingRef ?? null;
|
||||||
|
} catch (_) {
|
||||||
|
pendingRef = null;
|
||||||
|
}
|
||||||
|
options.captureApprove?.(pendingRef);
|
||||||
|
}
|
||||||
await route.fulfill({
|
await route.fulfill({
|
||||||
status: 200,
|
status: 200,
|
||||||
contentType: 'application/json',
|
contentType: 'application/json',
|
||||||
@@ -137,7 +147,9 @@ test.describe('UserFront WASM auth routing', () => {
|
|||||||
|
|
||||||
await page.goto('/ko/approve?ref=e2e-approve-ref');
|
await page.goto('/ko/approve?ref=e2e-approve-ref');
|
||||||
|
|
||||||
await expect(page).toHaveURL(/\/ko\/dashboard$/);
|
await expect(page).toHaveURL(/\/ko\/dashboard(?:\?.*)?$/, {
|
||||||
|
timeout: 10_000,
|
||||||
|
});
|
||||||
expect(approvedRef).toBe('e2e-approve-ref');
|
expect(approvedRef).toBe('e2e-approve-ref');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ const SIGNIN_SUBMIT_X = 640;
|
|||||||
const SIGNIN_SUBMIT_Y = 381;
|
const SIGNIN_SUBMIT_Y = 381;
|
||||||
|
|
||||||
const RESET_NEW_PASSWORD_X = 640;
|
const RESET_NEW_PASSWORD_X = 640;
|
||||||
const RESET_NEW_PASSWORD_Y = 401;
|
const RESET_NEW_PASSWORD_Y = 382;
|
||||||
const RESET_CONFIRM_PASSWORD_X = 640;
|
const RESET_CONFIRM_PASSWORD_X = 640;
|
||||||
const RESET_CONFIRM_PASSWORD_Y = 464;
|
const RESET_CONFIRM_PASSWORD_Y = 464;
|
||||||
const RESET_SUBMIT_X = 640;
|
const RESET_SUBMIT_X = 640;
|
||||||
@@ -236,7 +236,13 @@ test.describe('UserFront WASM password login and reset', () => {
|
|||||||
const capture: RequestCapture = { clientLogs: [] };
|
const capture: RequestCapture = { clientLogs: [] };
|
||||||
await mockAuthApis(page, capture);
|
await mockAuthApis(page, capture);
|
||||||
|
|
||||||
|
const policyLoaded = page.waitForResponse(
|
||||||
|
(response) =>
|
||||||
|
response.url().includes('/api/v1/auth/password/policy') &&
|
||||||
|
response.status() === 200,
|
||||||
|
);
|
||||||
await page.goto('/ko/reset-password?token=reset-token-e2e');
|
await page.goto('/ko/reset-password?token=reset-token-e2e');
|
||||||
|
await policyLoaded;
|
||||||
await page.waitForTimeout(900);
|
await page.waitForTimeout(900);
|
||||||
await fillAt(page, RESET_NEW_PASSWORD_X, RESET_NEW_PASSWORD_Y, 'ValidPass1!A');
|
await fillAt(page, RESET_NEW_PASSWORD_X, RESET_NEW_PASSWORD_Y, 'ValidPass1!A');
|
||||||
await fillAt(
|
await fillAt(
|
||||||
@@ -250,7 +256,10 @@ test.describe('UserFront WASM password login and reset', () => {
|
|||||||
force: true,
|
force: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
await expect(page).toHaveURL(/\/ko\/signin$/);
|
await expect
|
||||||
|
.poll(() => capture.resetBody?.newPassword as string | undefined)
|
||||||
|
.toBe('ValidPass1!A');
|
||||||
|
await expect(page).toHaveURL(/\/ko\/signin(?:\?.*)?$/, { timeout: 10_000 });
|
||||||
expect(capture.resetToken).toBe('reset-token-e2e');
|
expect(capture.resetToken).toBe('reset-token-e2e');
|
||||||
expect(capture.resetBody?.newPassword).toBe('ValidPass1!A');
|
expect(capture.resetBody?.newPassword).toBe('ValidPass1!A');
|
||||||
});
|
});
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -80,6 +80,7 @@ title_with_code = ""
|
|||||||
type = ""
|
type = ""
|
||||||
|
|
||||||
[msg.userfront.error.whitelist]
|
[msg.userfront.error.whitelist]
|
||||||
|
"$normalizedCode" = ""
|
||||||
settings_disabled = ""
|
settings_disabled = ""
|
||||||
invalid_session = ""
|
invalid_session = ""
|
||||||
verification_required = ""
|
verification_required = ""
|
||||||
@@ -91,6 +92,7 @@ bad_request = ""
|
|||||||
password_or_email_mismatch = ""
|
password_or_email_mismatch = ""
|
||||||
|
|
||||||
[msg.userfront.error.ory]
|
[msg.userfront.error.ory]
|
||||||
|
"$normalizedCode" = ""
|
||||||
access_denied = ""
|
access_denied = ""
|
||||||
consent_required = ""
|
consent_required = ""
|
||||||
interaction_required = ""
|
interaction_required = ""
|
||||||
@@ -558,3 +560,13 @@ verify = ""
|
|||||||
|
|
||||||
[ui.userfront.signup.success]
|
[ui.userfront.signup.success]
|
||||||
action = ""
|
action = ""
|
||||||
|
|
||||||
|
|
||||||
|
# Auto-added missing keys
|
||||||
|
|
||||||
|
[ui.common]
|
||||||
|
admin_only = ""
|
||||||
|
assign = ""
|
||||||
|
none = ""
|
||||||
|
select = ""
|
||||||
|
select_placeholder = ""
|
||||||
|
|||||||
@@ -143,4 +143,54 @@ void main() {
|
|||||||
expect(find.text('원문 메시지'), findsNothing);
|
expect(find.text('원문 메시지'), findsNothing);
|
||||||
expect(find.text(type), findsOneWidget);
|
expect(find.text(type), findsOneWidget);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets('프로덕션은 not_found 코드를 whitelist 메시지로 노출한다 (404 매핑)', (
|
||||||
|
WidgetTester tester,
|
||||||
|
) async {
|
||||||
|
await _pumpErrorScreen(
|
||||||
|
tester,
|
||||||
|
errorCode: 'not_found',
|
||||||
|
description: '원문 메시지',
|
||||||
|
isProdOverride: true,
|
||||||
|
);
|
||||||
|
|
||||||
|
final detail = tr(
|
||||||
|
'msg.userfront.error.whitelist.not_found',
|
||||||
|
fallback: internalErrorWhitelistMessages['not_found']!,
|
||||||
|
);
|
||||||
|
final type = tr(
|
||||||
|
'msg.userfront.error.type',
|
||||||
|
fallback: '오류 종류: {{type}}',
|
||||||
|
params: {'type': 'not_found'},
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(find.text(detail), findsOneWidget);
|
||||||
|
expect(find.text(type), findsOneWidget);
|
||||||
|
expect(find.text('원문 메시지'), findsNothing);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('프로덕션은 rate_limited 코드를 whitelist 메시지로 노출한다 (429 매핑)', (
|
||||||
|
WidgetTester tester,
|
||||||
|
) async {
|
||||||
|
await _pumpErrorScreen(
|
||||||
|
tester,
|
||||||
|
errorCode: 'rate_limited',
|
||||||
|
description: '원문 메시지',
|
||||||
|
isProdOverride: true,
|
||||||
|
);
|
||||||
|
|
||||||
|
final detail = tr(
|
||||||
|
'msg.userfront.error.whitelist.rate_limited',
|
||||||
|
fallback: internalErrorWhitelistMessages['rate_limited']!,
|
||||||
|
);
|
||||||
|
final type = tr(
|
||||||
|
'msg.userfront.error.type',
|
||||||
|
fallback: '오류 종류: {{type}}',
|
||||||
|
params: {'type': 'rate_limited'},
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(find.text(detail), findsOneWidget);
|
||||||
|
expect(find.text(type), findsOneWidget);
|
||||||
|
expect(find.text('원문 메시지'), findsNothing);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user