JJ_MKVMaker/test/youtube_ads_test.dart

92 lines
4.1 KiB
Dart

import 'dart:convert';
import 'dart:io';
import 'package:flutter_test/flutter_test.dart';
import 'package:jj_mkvmaker/app/settings.dart';
import 'package:jj_mkvmaker/core/youtube_ads.dart';
import 'package:path/path.dart' as p;
/// 가짜 YouTube 페이지 (DOM 흉내) 에서 스크립트를 실제로 돌려 본다 (node 가 있을 때)
const _harness = r'''
var timers = [];
global.setInterval = function (f) { timers.push(f); return timers.length; };
function el(cls) {
return { classList: { _c: cls.slice(), contains: function (x) { return this._c.indexOf(x) >= 0; },
remove: function (x) { this._c = this._c.filter(function (y) { return y !== x; }); } },
clicked: 0, click: function () { this.clicked++; }, style: {}, remove: function () { this.removed = 1; } };
}
var player = el(['html5-video-player', 'ad-showing']);
var skip = el(['ytp-skip-ad-button']);
var video = { muted: false, playbackRate: 1.5, duration: 30, currentTime: 2 };
var appended = [];
var body = { appendChild: function (n) { appended.push(n); } };
global.location = { hostname: process.argv[3] };
global.document = {
body: body, head: body, documentElement: body,
createElement: function () { return el([]); },
querySelector: function (q) {
if (q === 'video') return video;
if (q === '.html5-video-player') return player;
if (q.indexOf('skip') >= 0) return player.classList.contains('ad-showing') ? skip : null;
return null;
},
};
global.window = global;
require(process.argv[2]);
var r = { installed: timers.length };
r.skipClicked = skip.clicked; r.muted = video.muted; r.rate = video.playbackRate; r.time = video.currentTime;
r.badge = appended.some(function (n) { return n.textContent === '광고 건너뛰는 중…'; });
r.style = appended.some(function (n) { return (n.textContent || '').indexOf('display:none') >= 0; });
player.classList.remove('ad-showing');
timers.forEach(function (f) { f(); });
r.afterMuted = video.muted; r.afterRate = video.playbackRate;
console.log(JSON.stringify(r));
''';
void main() {
test('설정: 광고 건너뛰기 · 숨기기 기본 켜짐, 저장', () {
final s = AppSettings();
expect([s.browser.adSkip, s.browser.adHide], [true, true]);
s
..browser.adSkip = false
..browser.adHide = false;
final back = AppSettings.fromJson(s.toJson());
expect([back.browser.adSkip, back.browser.adHide], [false, false]);
expect(youtubeAdScript(skip: false, hide: true), contains('{skip: false, hide: true}'));
});
final node = Platform.isWindows ? r'C:\Program Files\nodejs\node.exe' : '/usr/bin/node';
test('가짜 YouTube 페이지: 건너뛰기 버튼 누르기 · 소리 끄고 빨리 감기 · 끝나면 되돌리기', () async {
final dir = Directory.systemTemp.createTempSync('jj_ads_');
addTearDown(() => dir.deleteSync(recursive: true));
Future<Map<String, Object?>> run(String host, {bool skip = true, bool hide = true}) async {
final script = File(p.join(dir.path, 'ad_${host}_$skip$hide.js'))
..writeAsStringSync(youtubeAdScript(skip: skip, hide: hide));
final harness = File(p.join(dir.path, 'harness.js'))..writeAsStringSync(_harness);
final r = await Process.run(node, [harness.path, script.path, host]);
expect(r.exitCode, 0, reason: '${r.stderr}');
return Map<String, Object?>.from(jsonDecode((r.stdout as String).trim()) as Map);
}
final yt = await run('www.youtube.com');
expect(yt['installed'], 1);
expect(yt['skipClicked'], 1);
expect(yt['muted'], true);
expect(yt['rate'], 16);
expect(yt['time'], closeTo(29.9, 0.01));
expect(yt['badge'], true);
expect(yt['style'], true);
// 광고가 끝나면 원래 소리 · 속도로
expect(yt['afterMuted'], false);
expect(yt['afterRate'], 1.5);
// 끄면 아무것도 하지 않는다
final off = await run('m.youtube.com', skip: false, hide: false);
expect([off['skipClicked'], off['muted'], off['style']], [0, false, false]);
// YouTube 가 아닌 사이트에는 넣지 않는다
final other = await run('example.com');
expect(other['installed'], 0);
}, skip: File(node).existsSync() ? false : 'node 없음');
}