125 lines
6.1 KiB
Dart
125 lines
6.1 KiB
Dart
import 'dart:math';
|
|
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:jj_mkvmaker/app/app_controller.dart';
|
|
import 'package:jj_mkvmaker/app/settings.dart';
|
|
import 'package:jj_mkvmaker/main.dart';
|
|
import 'package:jj_mkvmaker/platform/windows/desktop_storage_service.dart';
|
|
import 'package:jj_mkvmaker/platform/windows/process_media_tool.dart';
|
|
import 'package:jj_mkvmaker/services/platform_services.dart';
|
|
import 'package:jj_mkvmaker/ui/theme.dart';
|
|
|
|
/// X-plore 비교 5-1 ⑦ 밝은 테마 (관리자 지시 10/21): 처음 값 어둡게 (예전 그대로) · 밝게 · 기기 따라 · 바꾸면 상태는 그대로 다시 그림
|
|
double _lum(Color c) {
|
|
double ch(double v) => v <= 0.03928 ? v / 12.92 : pow((v + 0.055) / 1.055, 2.4).toDouble();
|
|
return 0.2126 * ch(c.r) + 0.7152 * ch(c.g) + 0.0722 * ch(c.b);
|
|
}
|
|
|
|
/// WCAG 대비
|
|
double contrast(Color a, Color b) {
|
|
final x = _lum(a), y = _lum(b);
|
|
return (max(x, y) + 0.05) / (min(x, y) + 0.05);
|
|
}
|
|
|
|
class _Probe extends StatefulWidget {
|
|
const _Probe();
|
|
@override
|
|
State<_Probe> createState() => _ProbeState();
|
|
}
|
|
|
|
class _ProbeState extends State<_Probe> {
|
|
int taps = 0;
|
|
@override
|
|
Widget build(BuildContext context) => GestureDetector(
|
|
onTap: () => setState(() => taps++),
|
|
child: Text('probe $taps', style: TextStyle(color: JjColors.text)),
|
|
);
|
|
}
|
|
|
|
void main() {
|
|
tearDown(() => JjColors.use(Brightness.dark));
|
|
|
|
test('처음 값은 어둡게 (예전 색 그대로) · 모르는 값 · 없는 값도 어둡게 · 고른 값은 그대로', () {
|
|
expect(AppSettings().uiBrightness, 'dark');
|
|
expect(AppSettings.fromJson({}).uiBrightness, 'dark');
|
|
expect(AppSettings.fromJson({'uiBrightness': 'neon'}).uiBrightness, 'dark');
|
|
expect(AppSettings.fromJson({'uiBrightness': 'light'}).uiBrightness, 'light');
|
|
expect(AppSettings.fromJson(AppSettings.fromJson({'uiBrightness': 'system'}).toJson()).uiBrightness, 'system');
|
|
expect(JjColors.bg, const Color(0xFF141417));
|
|
expect(JjColors.accent, const Color(0xFF00C4CC));
|
|
expect(JjColors.textDim, const Color(0xFF8E8E99));
|
|
// 예전에 쓰던 Material 색 그대로 (어두운 화면이 바뀌지 않게)
|
|
expect([JjColors.warning, JjColors.alert, JjColors.amber, JjColors.amberText, JjColors.positive], [Colors.orangeAccent, Colors.redAccent, Colors.amber, Colors.amber, Colors.greenAccent].map((c) => Color(c.toARGB32())).toList());
|
|
expect(resolveBrightness('dark', Brightness.light), Brightness.dark);
|
|
expect(resolveBrightness('light', Brightness.dark), Brightness.light);
|
|
expect(resolveBrightness('system', Brightness.light), Brightness.light);
|
|
expect(resolveBrightness('?', Brightness.light), Brightness.dark);
|
|
});
|
|
|
|
test('밝은 색: 바탕 · 칸 위 글자 대비 4.5:1 이상 (글 · 흐린 글 · 강조 · 경고 · 성공 · 강조 위 글자)', () {
|
|
const l = JjPalette.light;
|
|
for (final bg in [l.bg, l.panel, l.panelHigh]) {
|
|
for (final (name, fg) in [('text', l.text), ('textDim', l.textDim), ('accent', l.accent), ('danger', l.danger), ('success', l.success), ('warning', l.warning), ('alert', l.alert), ('amberText', l.amberText), ('positive', l.positive)]) {
|
|
expect(contrast(fg, bg), greaterThanOrEqualTo(4.5), reason: '$name on ${bg.toARGB32().toRadixString(16)}');
|
|
}
|
|
}
|
|
expect(contrast(l.onAccent, l.accent), greaterThanOrEqualTo(4.5));
|
|
// 아이콘 (폴더 · 별) 은 3:1
|
|
for (final bg in [l.bg, l.panel, l.panelHigh]) {
|
|
expect(contrast(l.amber, bg), greaterThanOrEqualTo(3));
|
|
}
|
|
expect(buildTheme(Brightness.light).brightness, Brightness.light);
|
|
expect(buildTheme(Brightness.light).scaffoldBackgroundColor, l.bg);
|
|
expect(buildTheme(Brightness.dark).scaffoldBackgroundColor, JjPalette.dark.bg);
|
|
});
|
|
|
|
testWidgets('색을 바꾸면 JjColors 를 직접 읽는 곳도 다시 그림 · 상태는 그대로', (t) async {
|
|
await t.pumpWidget(const MaterialApp(home: Scaffold(body: _Probe())));
|
|
await t.tap(find.text('probe 0'));
|
|
await t.pump();
|
|
expect(t.widget<Text>(find.text('probe 1')).style!.color, JjPalette.dark.text);
|
|
expect(JjColors.use(Brightness.light), isTrue);
|
|
expect(JjColors.use(Brightness.light), isFalse, reason: '같으면 바꾸지 않음');
|
|
rebuildAllElements(t.binding.rootElement!);
|
|
await t.pump();
|
|
expect(t.widget<Text>(find.text('probe 1')).style!.color, JjPalette.light.text, reason: '눌러 둔 수 (상태) 는 그대로');
|
|
});
|
|
|
|
testWidgets('앱: 설정 "화면 밝기" 를 밝게 → 바로 밝은 테마 · 기기 따라는 기기 밝기를 따라 바뀜 · 어둡게로 돌아옴', (t) async {
|
|
t.view.physicalSize = const Size(1400, 900);
|
|
t.view.devicePixelRatio = 1;
|
|
addTearDown(t.view.reset);
|
|
addTearDown(t.platformDispatcher.clearPlatformBrightnessTestValue);
|
|
final c = AppController(PlatformServices(mediaTool: ProcessMediaTool('x', 'y'), storage: DesktopStorageService()));
|
|
await t.runAsync(c.init);
|
|
await t.pumpWidget(JjMkvMakerApp(controller: c));
|
|
await t.pump();
|
|
BuildContext ctx() => t.element(find.byType(Scaffold).first);
|
|
expect(Theme.of(ctx()).brightness, Brightness.dark);
|
|
expect(JjColors.isLight, isFalse);
|
|
await t.runAsync(() => c.updateSettings((x) => x.uiBrightness = 'light'));
|
|
await t.pump();
|
|
await t.pump();
|
|
expect(JjColors.isLight, isTrue);
|
|
expect(Theme.of(ctx()).brightness, Brightness.light);
|
|
expect(Theme.of(ctx()).scaffoldBackgroundColor, JjPalette.light.bg);
|
|
// 기기 따라
|
|
t.platformDispatcher.platformBrightnessTestValue = Brightness.dark;
|
|
await t.runAsync(() => c.updateSettings((x) => x.uiBrightness = 'system'));
|
|
await t.pump();
|
|
await t.pump();
|
|
expect(JjColors.isLight, isFalse);
|
|
t.platformDispatcher.platformBrightnessTestValue = Brightness.light;
|
|
await t.pump();
|
|
await t.pump();
|
|
expect(JjColors.isLight, isTrue, reason: '기기 밝기가 바뀌면 따라감');
|
|
await t.runAsync(() => c.updateSettings((x) => x.uiBrightness = 'dark'));
|
|
await t.pump();
|
|
await t.pump();
|
|
expect(JjColors.isLight, isFalse);
|
|
expect(Theme.of(ctx()).brightness, Brightness.dark);
|
|
await t.pumpWidget(const SizedBox());
|
|
});
|
|
}
|