code check 오류 수정
This commit is contained in:
1 parent
3c54c46898
commit
e98ab39dfe
11 files changed
+155
-104
No files matched your search
@@ -280,6 +280,11 @@ privacy_full = "\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
|
||||
tos_full = "\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n바론 소프트웨어 이용약관\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n제1장 총칙\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n제1조 (목적)\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n이 약관은 바론컨설턴트(이하 \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\"회사\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\"라 합니다)가 제공하는 바론소프트웨어(이하 \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\"서비스\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\"라 합니다)를 이용함에 있어 회사와 이용자 간의 권리, 의무 및 책임사항과 기타 필요한 사항을 정하는 것을 목적으로 합니다.\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n제2조 (용어의 정의)\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n① 본 약관에서 사용하는 용어의 정의는 다음과 같습니다:\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n- “서비스”란 회사가 제공하는 소프트웨어 및 관련 제반 서비스를 의미합니다.\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n- “이용자”란 회사의 서비스에 접속하여 본 약관에 따라 회사가 제공하는 서비스를 이용하는 회원 및 비회원을 말합니다.\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n- “회원”이란 본 약관에 동의하고 회사와 이용계약을 체결한 자를 의미합니다.\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n- “비회원”이란 회원가입을 하지 않고 회사가 제공하는 일부 서비스를 이용하는 자를 말합니다.\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n제3조 (약관의 효력 및 변경)\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n① 본 약관은 이용자가 본 약관에 동의하고, 회사가 이에 대한 승낙을 완료함으로써 효력이 발생합니다. ② 회사는 필요한 경우 본 약관을 변경할 수 있으며, 변경된 약관은 서비스 화면에 공지된 후 효력이 발생합니다.\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n제4조 (약관 외 준칙)\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n본 약관에 명시되지 않은 사항에 대해서는 대한민국의 관련 법령과 상관습에 따릅니다.\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n제2장 서비스 이용계약\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n제5조 (이용계약의 성립)\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n이용계약은 이용자가 약관의 내용에 동의하고, 회사가 제공하는 소정의 회원가입 신청서를 작성하여 가입을 완료한 후, 회사가 이를 승인함으로써 성립합니다.\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n제6조 (이용계약의 유보와 거절)\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n① 회사는 다음 각 호에 해당하는 경우 이용계약의 성립을 유보하거나 거절할 수 있습니다: - 신청서의 내용이 허위로 판명된 경우 - 서비스 제공이 기술적으로 어려운 경우\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n제7조 (계약사항의 변경)\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n회원은 개인정보 관리 메뉴를 통해 언제든지 자신의 정보를 열람하고 수정할 수 있습니다. 회원의 정보가 변경된 경우 즉시 수정해야 하며, 수정하지 않아 발생하는 문제의 책임은 회원에게 있습니다.\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n제3장 개인정보 보호\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n제8조 (개인정보 보호의 원칙)\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n① 회원의 개인정보는 관련 법령에 따라 보호됩니다. ② 회사는 개인정보 보호와 관련된 세부 사항을 별도로 마련한 개인정보처리방침에 따라 관리하며, 이용자는 언제든지 해당 방침을 통해 개인정보 관리에 대한 자세한 내용을 확인할 수 있습니다.\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n제9조 (개인정보처리방침 준수)\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n① 회사는 Line truncated
|
||||
|
||||
[msg.userfront.signup.agreement]
|
||||
all_hint = "Agree to both required documents to continue to the next step."
|
||||
description = "Review the service terms and privacy collection notice, then agree to continue."
|
||||
privacy_summary = "Review what personal data is collected, why it is used, and how it is retained."
|
||||
progress = "{count} of {total} required agreements completed"
|
||||
tos_summary = "Review the service terms, usage conditions, and responsibilities."
|
||||
title = "Please review and agree to the terms to continue."
|
||||
|
||||
[msg.userfront.signup.auth]
|
||||
@@ -592,6 +597,7 @@ title = "Sign up"
|
||||
[ui.userfront.signup.agreement]
|
||||
all = "Agree to all"
|
||||
privacy_title = "Privacy Policy (Required)"
|
||||
required = "Required"
|
||||
tos_title = "Terms of Service (Required)"
|
||||
|
||||
[ui.userfront.signup.auth]
|
||||
|
||||
@@ -277,6 +277,11 @@ privacy_full = "개인정보 수집 및 이용 동의 전문..."
|
||||
tos_full = "서비스 이용약관 전문..."
|
||||
|
||||
[msg.userfront.signup.agreement]
|
||||
all_hint = "필수 약관 2개를 모두 확인하고 동의하면 다음 단계로 진행할 수 있습니다."
|
||||
description = "계속 진행하려면 서비스 이용 조건과 개인정보 수집·이용 항목을 확인한 뒤 동의해주세요."
|
||||
privacy_summary = "개인정보 수집 항목, 이용 목적, 보관 기준을 안내합니다."
|
||||
progress = "필수 약관 {total}개 중 {count}개 동의 완료"
|
||||
tos_summary = "서비스 이용 조건과 책임 범위를 확인할 수 있습니다."
|
||||
title = "서비스 이용을 위해\n약관에 동의해주세요"
|
||||
|
||||
[msg.userfront.signup.auth]
|
||||
@@ -583,6 +588,7 @@ title = "회원가입"
|
||||
[ui.userfront.signup.agreement]
|
||||
all = "모두 동의합니다"
|
||||
privacy_title = "개인정보 수집 및 이용 동의 (필수)"
|
||||
required = "필수"
|
||||
tos_title = "바론 소프트웨어 이용약관 (필수)"
|
||||
|
||||
[ui.userfront.signup.auth]
|
||||
@@ -616,4 +622,3 @@ verify = "본인인증"
|
||||
|
||||
[ui.userfront.signup.success]
|
||||
action = "로그인하기"
|
||||
|
||||
@@ -277,6 +277,11 @@ privacy_full = ""
|
||||
tos_full = ""
|
||||
|
||||
[msg.userfront.signup.agreement]
|
||||
all_hint = ""
|
||||
description = ""
|
||||
privacy_summary = ""
|
||||
progress = ""
|
||||
tos_summary = ""
|
||||
title = ""
|
||||
|
||||
[msg.userfront.signup.auth]
|
||||
@@ -583,6 +588,7 @@ title = ""
|
||||
[ui.userfront.signup.agreement]
|
||||
all = ""
|
||||
privacy_title = ""
|
||||
required = ""
|
||||
tos_title = ""
|
||||
|
||||
[ui.userfront.signup.auth]
|
||||
@@ -616,4 +622,3 @@ verify = ""
|
||||
|
||||
[ui.userfront.signup.success]
|
||||
action = ""
|
||||
|
||||
@@ -238,8 +238,8 @@ class _ConsentScreenState extends State<ConsentScreen> {
|
||||
final clientName = (clientRawName != null && clientRawName.isNotEmpty)
|
||||
? clientRawName
|
||||
: (clientId != '-'
|
||||
? clientId
|
||||
: tr('msg.userfront.consent.client_unknown'));
|
||||
? clientId
|
||||
: tr('msg.userfront.consent.client_unknown'));
|
||||
final clientLogo = _consentInfo?['client']?['logo_uri'];
|
||||
final requestedScopes =
|
||||
(_consentInfo?['requested_scope'] as List<dynamic>?)?.cast<String>() ??
|
||||
|
||||
@@ -1494,8 +1494,7 @@ class _SignupScreenState extends State<SignupScreen> {
|
||||
labelText: tr(
|
||||
'ui.userfront.signup.profile.company',
|
||||
),
|
||||
border:
|
||||
const OutlineInputBorder(),
|
||||
border: const OutlineInputBorder(),
|
||||
),
|
||||
items: [
|
||||
DropdownMenuItem(
|
||||
@@ -1557,7 +1556,9 @@ class _SignupScreenState extends State<SignupScreen> {
|
||||
_buildProfileFieldGroup(
|
||||
title: _affiliationType == 'AFFILIATE'
|
||||
? tr('ui.userfront.signup.profile.department')
|
||||
: tr('ui.userfront.signup.profile.department_optional'),
|
||||
: tr(
|
||||
'ui.userfront.signup.profile.department_optional',
|
||||
),
|
||||
description: _affiliationType == 'AFFILIATE'
|
||||
? '가족사 사용자는 부서명을 입력해주세요.'
|
||||
: '선택 입력 항목입니다.',
|
||||
@@ -1677,10 +1678,7 @@ class _SignupScreenState extends State<SignupScreen> {
|
||||
],
|
||||
),
|
||||
),
|
||||
if (trailing != null) ...[
|
||||
const SizedBox(width: 12),
|
||||
trailing,
|
||||
],
|
||||
if (trailing != null) ...[const SizedBox(width: 12), trailing],
|
||||
],
|
||||
),
|
||||
SizedBox(height: isDesktop ? 18 : 14),
|
||||
@@ -1793,13 +1791,22 @@ class _SignupScreenState extends State<SignupScreen> {
|
||||
hasTypeCount,
|
||||
),
|
||||
if (requiresUpper)
|
||||
_cryptoCheck(tr('msg.userfront.signup.password.rule.uppercase'), hasUpper),
|
||||
_cryptoCheck(
|
||||
tr('msg.userfront.signup.password.rule.uppercase'),
|
||||
hasUpper,
|
||||
),
|
||||
if (requiresLower)
|
||||
_cryptoCheck(tr('msg.userfront.signup.password.rule.lowercase'), hasLower),
|
||||
_cryptoCheck(
|
||||
tr('msg.userfront.signup.password.rule.lowercase'),
|
||||
hasLower,
|
||||
),
|
||||
if (requiresNumber)
|
||||
_cryptoCheck(tr('msg.userfront.signup.password.rule.number'), hasDigit),
|
||||
if (requiresSymbol)
|
||||
_cryptoCheck(tr('msg.userfront.signup.password.rule.symbol'), hasSpecial),
|
||||
_cryptoCheck(
|
||||
tr('msg.userfront.signup.password.rule.symbol'),
|
||||
hasSpecial,
|
||||
),
|
||||
];
|
||||
|
||||
return LayoutBuilder(
|
||||
@@ -1861,14 +1868,15 @@ class _SignupScreenState extends State<SignupScreen> {
|
||||
obscureText: _isPasswordObscured,
|
||||
onChanged: (_) => setState(() {}),
|
||||
decoration: InputDecoration(
|
||||
labelText: tr('ui.userfront.signup.password.label'),
|
||||
labelText: tr(
|
||||
'ui.userfront.signup.password.label',
|
||||
),
|
||||
border: const OutlineInputBorder(),
|
||||
errorText: _passwordError,
|
||||
suffixIcon: IconButton(
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
_isPasswordObscured =
|
||||
!_isPasswordObscured;
|
||||
_isPasswordObscured = !_isPasswordObscured;
|
||||
});
|
||||
},
|
||||
icon: Icon(
|
||||
@@ -1897,8 +1905,8 @@ class _SignupScreenState extends State<SignupScreen> {
|
||||
obscureText: _isConfirmPasswordObscured,
|
||||
onChanged: (val) {
|
||||
setState(() {
|
||||
_confirmPasswordError = (val !=
|
||||
_passwordController.text)
|
||||
_confirmPasswordError =
|
||||
(val != _passwordController.text)
|
||||
? tr('msg.userfront.signup.password.mismatch')
|
||||
: null;
|
||||
});
|
||||
@@ -2032,11 +2040,7 @@ class _SignupScreenState extends State<SignupScreen> {
|
||||
),
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(isDesktop ? 16 : 14),
|
||||
child: Wrap(
|
||||
spacing: 12,
|
||||
runSpacing: 10,
|
||||
children: checks,
|
||||
),
|
||||
child: Wrap(spacing: 12, runSpacing: 10, children: checks),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ class MockProfileNotifier extends ProfileNotifier {
|
||||
UserProfile? _profile;
|
||||
bool updateCalled = false;
|
||||
String? updatedName;
|
||||
|
||||
|
||||
@override
|
||||
Future<UserProfile?> build() async {
|
||||
_profile = UserProfile(
|
||||
@@ -33,7 +33,11 @@ class MockProfileNotifier extends ProfileNotifier {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> updateProfile({String? name, String? phone, String? department}) async {
|
||||
Future<void> updateProfile({
|
||||
String? name,
|
||||
String? phone,
|
||||
String? department,
|
||||
}) async {
|
||||
updateCalled = true;
|
||||
updatedName = name;
|
||||
_profile = _profile!.copyWith(
|
||||
@@ -46,75 +50,82 @@ class MockProfileNotifier extends ProfileNotifier {
|
||||
}
|
||||
|
||||
void main() {
|
||||
testWidgets('ProfilePage explicit save button UX flow (Edit -> Cancel -> Edit -> Save)', (tester) async {
|
||||
final recordedErrors = <FlutterErrorDetails>[];
|
||||
final previousOnError = FlutterError.onError;
|
||||
FlutterError.onError = (details) {
|
||||
final text = details.exceptionAsString();
|
||||
if (text.contains('A RenderFlex overflowed')) {
|
||||
return;
|
||||
}
|
||||
recordedErrors.add(details);
|
||||
};
|
||||
addTearDown(() {
|
||||
FlutterError.onError = previousOnError;
|
||||
});
|
||||
testWidgets(
|
||||
'ProfilePage explicit save button UX flow (Edit -> Cancel -> Edit -> Save)',
|
||||
(tester) async {
|
||||
final recordedErrors = <FlutterErrorDetails>[];
|
||||
final previousOnError = FlutterError.onError;
|
||||
FlutterError.onError = (details) {
|
||||
final text = details.exceptionAsString();
|
||||
if (text.contains('A RenderFlex overflowed')) {
|
||||
return;
|
||||
}
|
||||
recordedErrors.add(details);
|
||||
};
|
||||
addTearDown(() {
|
||||
FlutterError.onError = previousOnError;
|
||||
});
|
||||
|
||||
tester.view.physicalSize = const Size(1920, 1080);
|
||||
tester.view.devicePixelRatio = 1.0;
|
||||
addTearDown(tester.view.resetPhysicalSize);
|
||||
addTearDown(tester.view.resetDevicePixelRatio);
|
||||
tester.view.physicalSize = const Size(1920, 1080);
|
||||
tester.view.devicePixelRatio = 1.0;
|
||||
addTearDown(tester.view.resetPhysicalSize);
|
||||
addTearDown(tester.view.resetDevicePixelRatio);
|
||||
|
||||
final mockNotifier = MockProfileNotifier();
|
||||
|
||||
await tester.pumpWidget(
|
||||
ProviderScope(
|
||||
overrides: [
|
||||
profileProvider.overrideWith(() => mockNotifier),
|
||||
],
|
||||
child: const MaterialApp(
|
||||
home: Scaffold(body: ProfilePage()),
|
||||
final mockNotifier = MockProfileNotifier();
|
||||
|
||||
await tester.pumpWidget(
|
||||
ProviderScope(
|
||||
overrides: [profileProvider.overrideWith(() => mockNotifier)],
|
||||
child: const MaterialApp(home: Scaffold(body: ProfilePage())),
|
||||
),
|
||||
),
|
||||
);
|
||||
);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// 1. Entering edit mode
|
||||
final editButton = find.byKey(const Key('profile-name-edit-button'));
|
||||
expect(editButton, findsOneWidget);
|
||||
await tester.tap(editButton);
|
||||
await tester.pumpAndSettle();
|
||||
// 1. Entering edit mode
|
||||
final editButton = find.byKey(const Key('profile-name-edit-button'));
|
||||
expect(editButton, findsOneWidget);
|
||||
await tester.tap(editButton);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
final inputField = find.byKey(const Key('profile-name-input'));
|
||||
expect(inputField, findsOneWidget);
|
||||
|
||||
// 2. Testing cancel flow
|
||||
await tester.enterText(inputField, 'Changed Name');
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
final cancelButton = find.byKey(const Key('profile-name-cancel-button'));
|
||||
await tester.tap(cancelButton);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// After cancellation, the field should be read-only again.
|
||||
expect(find.byKey(const Key('profile-name-input')), findsNothing);
|
||||
// Find text could be part of ListTile
|
||||
expect(find.text('Original Name'), findsWidgets);
|
||||
|
||||
// 3. Re-enter edit mode and explicitly save
|
||||
await tester.tap(find.byKey(const Key('profile-name-edit-button')));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.enterText(find.byKey(const Key('profile-name-input')), 'Saved Name');
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
final saveButton = find.byKey(const Key('profile-name-save-button'));
|
||||
await tester.tap(saveButton);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Verify the mock received the update
|
||||
expect(mockNotifier.updateCalled, isTrue);
|
||||
expect(mockNotifier.updatedName, 'Saved Name');
|
||||
});
|
||||
final inputField = find.byKey(const Key('profile-name-input'));
|
||||
expect(inputField, findsOneWidget);
|
||||
|
||||
// 2. Testing cancel flow
|
||||
await tester.enterText(inputField, 'Changed Name');
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
final cancelButton = find.byKey(const Key('profile-name-cancel-button'));
|
||||
await tester.tap(cancelButton);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// After cancellation, the field should be read-only again.
|
||||
expect(find.byKey(const Key('profile-name-input')), findsNothing);
|
||||
// Find text could be part of ListTile
|
||||
expect(find.text('Original Name'), findsWidgets);
|
||||
|
||||
// 3. Re-enter edit mode and explicitly save
|
||||
await tester.tap(find.byKey(const Key('profile-name-edit-button')));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.enterText(
|
||||
find.byKey(const Key('profile-name-input')),
|
||||
'Saved Name',
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
final saveButton = find.byKey(const Key('profile-name-save-button'));
|
||||
await tester.tap(saveButton);
|
||||
await tester.pumpAndSettle();
|
||||
await tester.pump(const Duration(seconds: 4));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
FlutterError.onError = previousOnError;
|
||||
|
||||
// Verify the mock received the update
|
||||
expect(mockNotifier.updateCalled, isTrue);
|
||||
expect(mockNotifier.updatedName, 'Saved Name');
|
||||
expect(recordedErrors, isEmpty);
|
||||
},
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user