forked from baron/baron-sso
feat: i18n 개선 및 userfront 로그인/로케일 보완
This commit is contained in:
5
userfront/test/helpers/web_storage.dart
Normal file
5
userfront/test/helpers/web_storage.dart
Normal file
@@ -0,0 +1,5 @@
|
||||
import 'web_storage_stub.dart'
|
||||
if (dart.library.html) 'web_storage_web.dart';
|
||||
|
||||
export 'web_storage_stub.dart'
|
||||
if (dart.library.html) 'web_storage_web.dart';
|
||||
21
userfront/test/helpers/web_storage_stub.dart
Normal file
21
userfront/test/helpers/web_storage_stub.dart
Normal file
@@ -0,0 +1,21 @@
|
||||
class WebStorage {
|
||||
bool get isWeb => false;
|
||||
|
||||
String? get(String key) => null;
|
||||
|
||||
void set(String key, String value) {}
|
||||
|
||||
String? getSession(String key) => null;
|
||||
|
||||
void setSession(String key, String value) {}
|
||||
|
||||
void removeSession(String key) {}
|
||||
|
||||
void clearSession() {}
|
||||
|
||||
void remove(String key) {}
|
||||
|
||||
void clear() {}
|
||||
}
|
||||
|
||||
final webStorage = WebStorage();
|
||||
37
userfront/test/helpers/web_storage_web.dart
Normal file
37
userfront/test/helpers/web_storage_web.dart
Normal file
@@ -0,0 +1,37 @@
|
||||
// ignore_for_file: avoid_web_libraries_in_flutter
|
||||
|
||||
import 'dart:html' as html;
|
||||
|
||||
class WebStorage {
|
||||
bool get isWeb => true;
|
||||
|
||||
String? get(String key) => html.window.localStorage[key];
|
||||
|
||||
void set(String key, String value) {
|
||||
html.window.localStorage[key] = value;
|
||||
}
|
||||
|
||||
String? getSession(String key) => html.window.sessionStorage[key];
|
||||
|
||||
void setSession(String key, String value) {
|
||||
html.window.sessionStorage[key] = value;
|
||||
}
|
||||
|
||||
void removeSession(String key) {
|
||||
html.window.sessionStorage.remove(key);
|
||||
}
|
||||
|
||||
void clearSession() {
|
||||
html.window.sessionStorage.clear();
|
||||
}
|
||||
|
||||
void remove(String key) {
|
||||
html.window.localStorage.remove(key);
|
||||
}
|
||||
|
||||
void clear() {
|
||||
html.window.localStorage.clear();
|
||||
}
|
||||
}
|
||||
|
||||
final webStorage = WebStorage();
|
||||
88
userfront/test/locale_storage_web_test.dart
Normal file
88
userfront/test/locale_storage_web_test.dart
Normal file
@@ -0,0 +1,88 @@
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:userfront/core/i18n/locale_storage.dart';
|
||||
import 'package:userfront/core/i18n/locale_storage_web.dart' as locale_web;
|
||||
|
||||
import 'helpers/web_storage.dart';
|
||||
|
||||
void main() {
|
||||
setUp(() {
|
||||
locale_web.LocaleStorageImpl.forceMemoryStorageForTests(false);
|
||||
locale_web.LocaleStorageImpl.forceSessionStorageForTests(false);
|
||||
if (webStorage.isWeb) {
|
||||
webStorage.clear();
|
||||
webStorage.clearSession();
|
||||
}
|
||||
});
|
||||
|
||||
tearDown(() {
|
||||
locale_web.LocaleStorageImpl.forceMemoryStorageForTests(false);
|
||||
locale_web.LocaleStorageImpl.forceSessionStorageForTests(false);
|
||||
if (webStorage.isWeb) {
|
||||
webStorage.clear();
|
||||
webStorage.clearSession();
|
||||
}
|
||||
});
|
||||
|
||||
test(
|
||||
'localStorage write/read (웹)',
|
||||
() {
|
||||
if (!webStorage.isWeb) {
|
||||
return;
|
||||
}
|
||||
|
||||
LocaleStorage.write('ko');
|
||||
expect(webStorage.get('locale'), 'ko');
|
||||
expect(LocaleStorage.read(), 'ko');
|
||||
},
|
||||
skip: !webStorage.isWeb,
|
||||
);
|
||||
|
||||
test(
|
||||
'legacy key에서 locale로 마이그레이션 (웹)',
|
||||
() {
|
||||
if (!webStorage.isWeb) {
|
||||
return;
|
||||
}
|
||||
|
||||
webStorage.set('baron_locale', 'en');
|
||||
expect(LocaleStorage.read(), 'en');
|
||||
expect(webStorage.get('locale'), 'en');
|
||||
expect(webStorage.get('baron_locale'), isNull);
|
||||
},
|
||||
skip: !webStorage.isWeb,
|
||||
);
|
||||
|
||||
test(
|
||||
'localStorage 접근이 차단되면 메모리 fallback (웹)',
|
||||
() {
|
||||
if (!webStorage.isWeb) {
|
||||
return;
|
||||
}
|
||||
|
||||
locale_web.LocaleStorageImpl.forceMemoryStorageForTests(true);
|
||||
|
||||
LocaleStorage.write('en');
|
||||
expect(webStorage.get('locale'), isNull);
|
||||
expect(webStorage.getSession('locale'), isNull);
|
||||
expect(LocaleStorage.read(), 'en');
|
||||
},
|
||||
skip: !webStorage.isWeb,
|
||||
);
|
||||
|
||||
test(
|
||||
'localStorage 접근이 차단되면 sessionStorage로 fallback (웹)',
|
||||
() {
|
||||
if (!webStorage.isWeb) {
|
||||
return;
|
||||
}
|
||||
|
||||
locale_web.LocaleStorageImpl.forceSessionStorageForTests(true);
|
||||
|
||||
LocaleStorage.write('ko');
|
||||
expect(webStorage.get('locale'), isNull);
|
||||
expect(webStorage.getSession('locale'), 'ko');
|
||||
expect(LocaleStorage.read(), 'ko');
|
||||
},
|
||||
skip: !webStorage.isWeb,
|
||||
);
|
||||
}
|
||||
66
userfront/test/locale_utils_test.dart
Normal file
66
userfront/test/locale_utils_test.dart
Normal file
@@ -0,0 +1,66 @@
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:userfront/core/i18n/locale_utils.dart';
|
||||
|
||||
void main() {
|
||||
group('locale_utils', () {
|
||||
test('normalizeLocaleCode handles supported locales', () {
|
||||
expect(normalizeLocaleCode('ko'), 'ko');
|
||||
expect(normalizeLocaleCode('ko-KR'), 'ko');
|
||||
expect(normalizeLocaleCode('en'), 'en');
|
||||
expect(normalizeLocaleCode('en-US'), 'en');
|
||||
});
|
||||
|
||||
test('normalizeLocaleCode falls back to default', () {
|
||||
expect(normalizeLocaleCode('ja'), defaultLocaleCode);
|
||||
expect(normalizeLocaleCode(null), defaultLocaleCode);
|
||||
expect(normalizeLocaleCode(''), defaultLocaleCode);
|
||||
});
|
||||
|
||||
test('extractLocaleFromPath picks locale when present', () {
|
||||
expect(extractLocaleFromPath(Uri.parse('/ko/signin')), 'ko');
|
||||
expect(extractLocaleFromPath(Uri.parse('/en/profile')), 'en');
|
||||
expect(extractLocaleFromPath(Uri.parse('/ko')), 'ko');
|
||||
});
|
||||
|
||||
test('extractLocaleFromPath returns null when missing', () {
|
||||
expect(extractLocaleFromPath(Uri.parse('/signin')), isNull);
|
||||
expect(extractLocaleFromPath(Uri.parse('/zz/signin')), isNull);
|
||||
});
|
||||
|
||||
test('stripLocalePath removes locale segment', () {
|
||||
expect(stripLocalePath(Uri.parse('/ko/signin')), '/signin');
|
||||
expect(stripLocalePath(Uri.parse('/en/profile')), '/profile');
|
||||
expect(stripLocalePath(Uri.parse('/ko')), '/');
|
||||
expect(stripLocalePath(Uri.parse('/en/')), '/');
|
||||
});
|
||||
|
||||
test('stripLocalePath keeps path without locale', () {
|
||||
expect(stripLocalePath(Uri.parse('/signin')), '/signin');
|
||||
expect(stripLocalePath(Uri.parse('/auth/callback')), '/auth/callback');
|
||||
});
|
||||
|
||||
test('buildLocalizedPath applies locale', () {
|
||||
expect(buildLocalizedPath('ko', Uri.parse('/signin')), '/ko/signin');
|
||||
expect(buildLocalizedPath('en', Uri.parse('/signin')), '/en/signin');
|
||||
expect(buildLocalizedPath('ko', Uri.parse('/')), '/ko');
|
||||
expect(buildLocalizedPath('en', Uri.parse('/')), '/en');
|
||||
});
|
||||
|
||||
test('buildLocalizedPath preserves query parameters', () {
|
||||
final uri = Uri.parse('/signin?redirect_uri=https://example.com');
|
||||
expect(
|
||||
buildLocalizedPath('ko', uri),
|
||||
'/ko/signin?redirect_uri=https%3A%2F%2Fexample.com',
|
||||
);
|
||||
});
|
||||
|
||||
test('buildLocalizedPath replaces existing locale', () {
|
||||
expect(buildLocalizedPath('en', Uri.parse('/ko/signin')), '/en/signin');
|
||||
expect(buildLocalizedPath('ko', Uri.parse('/en/profile')), '/ko/profile');
|
||||
});
|
||||
|
||||
test('buildLocalizedPath drops unknown 2-letter prefix', () {
|
||||
expect(buildLocalizedPath('ko', Uri.parse('/zz/signin')), '/ko/signin');
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -5,15 +5,36 @@
|
||||
// gestures. You can also use WidgetTester to find child widgets in the widget
|
||||
// tree, read text, and verify that the values of widget properties are correct.
|
||||
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import 'package:userfront/main.dart' show BaronSSOApp;
|
||||
|
||||
class _TestAssetLoader extends AssetLoader {
|
||||
const _TestAssetLoader();
|
||||
|
||||
@override
|
||||
Future<Map<String, dynamic>> load(String path, Locale locale) async {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
void main() {
|
||||
testWidgets('BaronSSOApp builds', (WidgetTester tester) async {
|
||||
// runApp에서 ProviderScope로 감싸서 쓰고 있으니 테스트도 동일하게 감쌈
|
||||
await tester.pumpWidget(const ProviderScope(child: BaronSSOApp()));
|
||||
await tester.pumpWidget(
|
||||
EasyLocalization(
|
||||
supportedLocales: const [Locale('en'), Locale('ko')],
|
||||
fallbackLocale: const Locale('en'),
|
||||
startLocale: const Locale('en'),
|
||||
path: 'assets/translations',
|
||||
assetLoader: const _TestAssetLoader(),
|
||||
child: const ProviderScope(child: BaronSSOApp()),
|
||||
),
|
||||
);
|
||||
await tester.pump(); // 한 프레임 더
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user