import 'dart:convert'; import 'package:flutter_test/flutter_test.dart'; import 'package:jinzhi_flutter_app/features/mall/application/mall_tab_config.dart'; import 'package:jinzhi_flutter_app/models/pb/jinzhi/v1/ads.pb.dart' as pb; void main() { test('builds the resident mall URL with Jinzhi attribution and theme', () { const config = MallTabConfig( enabled: true, url: 'https://mall.example.com/home?campaign=gold', adId: 'ad-1', trackingId: 'tracking-1', ); final uri = config.attributedUri(clickId: 'click-1', isDark: true)!; expect(uri.queryParameters['campaign'], 'gold'); expect(uri.queryParameters['source_app'], 'jinzhi'); expect(uri.queryParameters['source_entry'], 'mall_tab'); expect(uri.queryParameters['ad_id'], 'ad-1'); expect(uri.queryParameters['tracking_id'], 'tracking-1'); expect(uri.queryParameters['click_id'], 'click-1'); expect(uri.queryParameters['isNight'], '1'); }); test('retains server open-mode metadata', () { const resident = MallTabConfig( enabled: true, url: 'https://mall.example.com', ); const nativeApp = MallTabConfig( enabled: true, url: 'https://mall.example.com', openMode: 'native_app', ); const external = MallTabConfig( enabled: true, url: 'https://mall.example.com', openMode: 'external', ); expect(resident.opensInApp, isTrue); expect(nativeApp.opensInApp, isFalse); expect(external.opensInApp, isFalse); }); test('adds retail quote context to the mall URL for H5', () { const config = MallTabConfig( enabled: true, url: 'https://mall.example.com/home', ); final uri = config.attributedUri( clickId: 'quote-click', entry: MallEntryRequest.quoteRetailGold, )!; expect(uri.queryParameters['source_entry'], 'market_quote'); expect(uri.queryParameters['hook_type'], 'quote_cta'); expect(uri.queryParameters['entry_scene'], 'retail'); expect(uri.queryParameters['entry_metal'], 'au'); expect(uri.queryParameters['placement'], 'mall_tab'); }); test('shows the first-install promo only for a configured mall entry', () { const configured = MallTabConfig( enabled: true, url: 'https://mall.example.com', entryPromoEnabled: true, entryBadgeText: '新人礼', entryTipTitle: '新人礼:¥360 铂金订购券', entryTipBody: '点击这里进商城', ); const disabled = MallTabConfig( enabled: true, url: 'https://mall.example.com', entryPromoEnabled: false, entryTipTitle: '新人礼', ); expect(configured.showsEntryBadge, isTrue); expect(configured.showsEntryTip, isTrue); expect(disabled.showsEntryBadge, isFalse); expect(disabled.showsEntryTip, isFalse); }); test('parses the server-provided mall entry promotion fields', () { final creative = [ ..._stringField(9, 'https://mall.example.com'), ..._boolField(16, true), ..._stringField(17, '新人礼'), ..._stringField(18, '新人礼:¥360 铂金订购券'), ..._stringField(19, '点击这里进商城'), ]; final config = parseMallTabConfigResponse([ ..._boolField(1, true), ..._bytesField(2, creative), ]); expect(config.showsEntryBadge, isTrue); expect(config.entryBadgeText, '新人礼'); expect(config.entryTipTitle, '新人礼:¥360 铂金订购券'); expect(config.entryTipBody, '点击这里进商城'); }); test('parses generated mall promotion and open-mode fields', () { final response = pb.GetAdReply( enabled: true, ad: pb.AdCreative( adId: 'ad-1', title: '会员商城', h5Url: 'https://mall.example.com', trackingId: 'tracking-1', openMode: pb.AdOpenMode.AD_OPEN_MODE_NATIVE_APP, mallPromotion: pb.MallPromotion(quoteCtaEnabled: true), ), ); final config = parseMallTabConfigResponse(response.writeToBuffer()); expect(config.isVisible, isTrue); expect(config.openMode, 'native_app'); expect(config.quoteCtaEnabled, isTrue); expect(config.allowedHosts, {'mall.example.com'}); }); } List _stringField(int tag, String value) => _bytesField(tag, utf8.encode(value)); List _bytesField(int tag, List value) => [ ..._varint((tag << 3) | 2), ..._varint(value.length), ...value, ]; List _boolField(int tag, bool value) => [ ..._varint(tag << 3), value ? 1 : 0, ]; List _varint(int value) { final bytes = []; while (value > 127) { bytes.add((value & 127) | 128); value >>= 7; } bytes.add(value); return bytes; }