1
0
forked from baron/baron-sso

[WIP]모바일 로그인창 테스트 강화중

This commit is contained in:
2026-05-27 12:29:56 +09:00
parent 368f4bbad8
commit e240470d04
10 changed files with 199 additions and 20 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

View File

@@ -37,6 +37,7 @@ Map<String, dynamic> _normalizeFlatTranslations(Map<String, String> flatMap) =>
bool _isUserfrontTranslationKey(String key) { bool _isUserfrontTranslationKey(String key) {
return key.startsWith('domain.') || return key.startsWith('domain.') ||
key.startsWith('err.userfront.') ||
key.startsWith('msg.userfront.') || key.startsWith('msg.userfront.') ||
key.startsWith('ui.userfront.') || key.startsWith('ui.userfront.') ||
key.startsWith('ui.common.'); key.startsWith('ui.common.');

View File

@@ -52,14 +52,11 @@ class LogPolicy {
required String? appEnv, required String? appEnv,
required String? productionDebugFlag, required String? productionDebugFlag,
}) { }) {
final flag = parseOptionalBoolFlag(productionDebugFlag);
if (flag.specified) {
return flag.enabled;
}
if (!isProductionEnv(appEnv)) { if (!isProductionEnv(appEnv)) {
return true; return true;
} }
return false; final flag = parseOptionalBoolFlag(productionDebugFlag);
return flag.specified && flag.enabled;
} }
static bool shouldRelayClientLog({ static bool shouldRelayClientLog({
@@ -67,10 +64,12 @@ class LogPolicy {
required String? appEnv, required String? appEnv,
required String? productionDebugFlag, required String? productionDebugFlag,
}) { }) {
if (debugEnabled( final flag = parseOptionalBoolFlag(productionDebugFlag);
appEnv: appEnv, final debugRelayEnabled = isProductionEnv(appEnv)
productionDebugFlag: productionDebugFlag, ? flag.specified && flag.enabled
)) { : !(flag.specified && !flag.enabled);
if (debugRelayEnabled) {
return true; return true;
} }
final normalized = level.trim().toUpperCase(); final normalized = level.trim().toUpperCase();

View File

@@ -5,16 +5,98 @@ cd /workspace
/bin/sh ./scripts/sync_userfront_locales.sh /bin/sh ./scripts/sync_userfront_locales.sh
cd /workspace/userfront cd /workspace/userfront
USERFRONT_INTERNAL_PORT="${USERFRONT_INTERNAL_PORT:-5000}"
USERFRONT_FLUTTER_RUN_FLAGS="${USERFRONT_FLUTTER_RUN_FLAGS:---debug}"
USERFRONT_BOOT_WARMUP_ATTEMPTS="${USERFRONT_BOOT_WARMUP_ATTEMPTS:-120}"
USERFRONT_BOOT_WARMUP_INTERVAL_SECONDS="${USERFRONT_BOOT_WARMUP_INTERVAL_SECONDS:-0.5}"
USERFRONT_BOOT_WARMUP_LOCALES="${USERFRONT_BOOT_WARMUP_LOCALES:-ko en}"
USERFRONT_BOOT_WARMUP_VIEWPORTS="${USERFRONT_BOOT_WARMUP_VIEWPORTS:-mobile:390 desktop:1440}"
warm_get() {
path="$1"
locale="$2"
viewport="$3"
width="${viewport#*:}"
if [ "$width" = "$viewport" ]; then
width=""
fi
wget -qO- \
--header="Accept-Language: $locale" \
--header="Viewport-Width: $width" \
"http://127.0.0.1:${USERFRONT_INTERNAL_PORT}${path}" >/dev/null 2>&1
}
warm_userfront_once() {
flutter_pid="$1"
attempt=1
started_at="$(date +%s)"
while [ "$attempt" -le "$USERFRONT_BOOT_WARMUP_ATTEMPTS" ]; do
if wget -qO- "http://127.0.0.1:${USERFRONT_INTERNAL_PORT}/flutter_bootstrap.js" >/dev/null 2>&1; then
break
fi
if ! kill -0 "$flutter_pid" 2>/dev/null; then
echo "[userfront-boot] warmup skipped because flutter exited before readiness" >&2
return 0
fi
attempt=$((attempt + 1))
sleep "$USERFRONT_BOOT_WARMUP_INTERVAL_SECONDS"
done
if [ "$attempt" -gt "$USERFRONT_BOOT_WARMUP_ATTEMPTS" ]; then
echo "[userfront-boot] warmup skipped after ${USERFRONT_BOOT_WARMUP_ATTEMPTS} readiness attempts" >&2
return 0
fi
echo "[userfront-boot] one-shot warmup starting locales=\"${USERFRONT_BOOT_WARMUP_LOCALES}\" viewports=\"${USERFRONT_BOOT_WARMUP_VIEWPORTS}\"" >&2
for locale in $USERFRONT_BOOT_WARMUP_LOCALES; do
for viewport in $USERFRONT_BOOT_WARMUP_VIEWPORTS; do
warm_get "/${locale}/signin" "$locale" "$viewport" || true
done
done
for asset in \
/ \
/flutter_bootstrap.js \
/main.dart.mjs \
/main.dart.wasm \
/canvaskit/skwasm.js \
/canvaskit/skwasm.wasm \
/canvaskit/skwasm_heavy.js \
/canvaskit/skwasm_heavy.wasm \
/assets/AssetManifest.bin.json \
/assets/FontManifest.json
do
wget -qO- "http://127.0.0.1:${USERFRONT_INTERNAL_PORT}${asset}" >/dev/null 2>&1 || true
done
finished_at="$(date +%s)"
elapsed_seconds=$((finished_at - started_at))
echo "[userfront-boot] one-shot warmup completed in ${elapsed_seconds}s" >&2
}
set -- flutter run \ set -- flutter run \
-d web-server \ -d web-server \
--web-hostname 0.0.0.0 \ --web-hostname 0.0.0.0 \
--web-port "${USERFRONT_INTERNAL_PORT:-5000}" \ --web-port "${USERFRONT_INTERNAL_PORT}" \
--wasm \ --wasm \
--dart-define=BACKEND_URL="${BACKEND_URL:-}" \ --dart-define=BACKEND_URL="${BACKEND_URL:-}" \
--dart-define=CLIENT_LOG_DEBUG="${CLIENT_LOG_DEBUG:-false}" \ --dart-define=CLIENT_LOG_DEBUG="${CLIENT_LOG_DEBUG:-false}" \
--dart-define=APP_ENV="${APP_ENV:-dev}" \ --dart-define=APP_ENV="${APP_ENV:-dev}" \
--dart-define=USERFRONT_URL="${USERFRONT_URL:-}" \ --dart-define=USERFRONT_URL="${USERFRONT_URL:-}" \
${USERFRONT_FLUTTER_RUN_FLAGS:-} \ ${USERFRONT_FLUTTER_RUN_FLAGS} \
--no-web-resources-cdn --no-web-resources-cdn
exec "$@" "$@" &
flutter_pid="$!"
terminate() {
kill "$flutter_pid" 2>/dev/null || true
wait "$flutter_pid" 2>/dev/null || true
}
trap terminate INT TERM
warm_userfront_once "$flutter_pid"
wait "$flutter_pid"

View File

@@ -78,15 +78,18 @@ const canvasKitConfig = 'config:{canvasKitBaseUrl:"canvaskit/"}';
bootstrap = bootstrap.replace( bootstrap = bootstrap.replace(
/_flutter\.loader\.load\(\{\s*serviceWorkerSettings:\s*(\{[^{}]*\})\s*,\s*config:\s*\{[\s\S]*?serviceWorkerUrl[\s\S]*?\}\s*,\s*config:\s*\{[^}]*\}\s*\}\);/g, /_flutter\.loader\.load\(\{\s*serviceWorkerSettings:\s*(\{[^{}]*\})\s*,\s*config:\s*\{[\s\S]*?serviceWorkerUrl[\s\S]*?\}\s*,\s*config:\s*\{[^}]*\}\s*\}\);/g,
`_flutter.loader.load({${canvasKitConfig}});`, (_match, settings) =>
`_flutter.loader.load({serviceWorkerSettings:${ensureServiceWorkerUrl(settings)},${canvasKitConfig}});`,
); );
bootstrap = bootstrap.replace( bootstrap = bootstrap.replace(
/_flutter\.loader\.load\(\{\s*serviceWorkerSettings:\s*(\{[^{}]*\})\s*,\s*config:\s*\{[^}]*\}\s*\}\);/g, /_flutter\.loader\.load\(\{\s*serviceWorkerSettings:\s*(\{[^{}]*\})\s*,\s*config:\s*\{[^}]*\}\s*\}\);/g,
`_flutter.loader.load({${canvasKitConfig}});`, (_match, settings) =>
`_flutter.loader.load({serviceWorkerSettings:${ensureServiceWorkerUrl(settings)},${canvasKitConfig}});`,
); );
bootstrap = bootstrap.replace( bootstrap = bootstrap.replace(
/_flutter\.loader\.load\(\{\s*serviceWorkerSettings:\s*(\{[^{}]*\})\s*\}\);/g, /_flutter\.loader\.load\(\{\s*serviceWorkerSettings:\s*(\{[^{}]*\})\s*\}\);/g,
`_flutter.loader.load({${canvasKitConfig}});`, (_match, settings) =>
`_flutter.loader.load({serviceWorkerSettings:${ensureServiceWorkerUrl(settings)},${canvasKitConfig}});`,
); );
bootstrap = bootstrap.replace( bootstrap = bootstrap.replace(
/_flutter\.loader\.load\(\);/g, /_flutter\.loader\.load\(\);/g,
@@ -302,4 +305,34 @@ async function cacheFirst(request) {
`; `;
} }
function ensureServiceWorkerUrl(settings) {
const serviceWorkerUrl = `"/flutter_service_worker.js?v=" + ${serviceWorkerVersionExpression(settings)}`;
if (/serviceWorkerUrl\s*:/.test(settings)) {
return settings.replace(
/serviceWorkerUrl\s*:\s*[^,\n}]+,?/,
`serviceWorkerUrl: ${serviceWorkerUrl},`,
);
}
const closingBraceIndex = settings.lastIndexOf('}');
if (closingBraceIndex < 0) {
return settings;
}
const beforeClosing = settings.slice(0, closingBraceIndex).trimEnd();
const afterClosing = settings.slice(closingBraceIndex);
const separator =
beforeClosing.endsWith('{') || beforeClosing.endsWith(',') ? '' : ',';
return `${beforeClosing}${separator}
serviceWorkerUrl: ${serviceWorkerUrl},
${afterClosing}`;
}
function serviceWorkerVersionExpression(settings) {
const match = settings.match(/serviceWorkerVersion\s*:\s*([^,\n}]+)/);
return (
match?.[1]?.replace(/\/\*[\s\S]*?\*\//g, '').trim() ??
'serviceWorkerVersion'
);
}
console.log(`[userfront] optimized ${basename(buildDir)} with hashed entrypoints and brotli assets`); console.log(`[userfront] optimized ${basename(buildDir)} with hashed entrypoints and brotli assets`);

View File

@@ -14,7 +14,7 @@ void main() {
); );
}); });
test('explicit debug flag applies in development-like environment', () { test('explicit true enables debug in development-like environment', () {
expect( expect(
LogPolicy.debugEnabled(appEnv: 'dev', productionDebugFlag: 'true'), LogPolicy.debugEnabled(appEnv: 'dev', productionDebugFlag: 'true'),
isTrue, isTrue,
@@ -23,13 +23,16 @@ void main() {
LogPolicy.debugEnabled(appEnv: 'development', productionDebugFlag: '1'), LogPolicy.debugEnabled(appEnv: 'development', productionDebugFlag: '1'),
isTrue, isTrue,
); );
});
test('explicit false does not suppress local debug in development', () {
expect( expect(
LogPolicy.debugEnabled(appEnv: 'dev', productionDebugFlag: 'false'), LogPolicy.debugEnabled(appEnv: 'dev', productionDebugFlag: 'false'),
isFalse, isTrue,
); );
expect( expect(
LogPolicy.debugEnabled(appEnv: 'development', productionDebugFlag: '0'), LogPolicy.debugEnabled(appEnv: 'development', productionDebugFlag: '0'),
isFalse, isTrue,
); );
}); });

View File

@@ -43,6 +43,10 @@ void main() {
expect(translations['ui.admin.nav.api_keys'], isNull); expect(translations['ui.admin.nav.api_keys'], isNull);
expect(translations['ui.dev.console_title'], isNull); expect(translations['ui.dev.console_title'], isNull);
expect(
translations['err.userfront.auth_proxy.login_failed'],
'Login failed.',
);
expect(translations['ui.userfront.login.action.submit'], 'Sign in'); expect(translations['ui.userfront.login.action.submit'], 'Sign in');
expect(translations['ui.common.theme_light'], 'Light'); expect(translations['ui.common.theme_light'], 'Light');
}, },

View File

@@ -122,7 +122,7 @@
</main> </main>
<script> <script>
var baronBootstrapStartedAt = performance.now(); var baronBootstrapStartedAt = performance.now();
var baronMinimumShellMs = 1100; var baronMinimumShellMs = 0;
window.addEventListener("flutter-first-frame", function () { window.addEventListener("flutter-first-frame", function () {
var elapsedMs = performance.now() - baronBootstrapStartedAt; var elapsedMs = performance.now() - baronBootstrapStartedAt;
window.setTimeout( window.setTimeout(
@@ -136,6 +136,63 @@
); );
}); });
</script> </script>
<script src="flutter_bootstrap.js" async></script> <script>
(function () {
function loadFlutter() {
var script = document.createElement("script");
script.src = "flutter_bootstrap.js";
script.async = true;
document.body.appendChild(script);
}
var hostname = window.location.hostname;
var isLocalhost =
hostname === "localhost" || hostname === "127.0.0.1" || hostname === "::1";
if (!isLocalhost || !("serviceWorker" in navigator)) {
loadFlutter();
return;
}
navigator.serviceWorker
.getRegistrations()
.then(function (registrations) {
return Promise.all(
registrations.map(function (registration) {
return registration.unregister();
}),
);
})
.then(function () {
if (!window.caches) {
return;
}
return caches.keys().then(function (keys) {
return Promise.all(
keys
.filter(function (key) {
return (
key.indexOf("baron-userfront-") === 0 ||
key.indexOf("flutter-app-cache") === 0
);
})
.map(function (key) {
return caches.delete(key);
}),
);
});
})
.then(function () {
if (navigator.serviceWorker.controller) {
window.location.reload();
return;
}
loadFlutter();
})
.catch(function (error) {
console.warn("[baron] failed to clear local service worker", error);
loadFlutter();
});
})();
</script>
</body> </body>
</html> </html>