import 'package:flutter_test/flutter_test.dart'; import 'package:yx_app_upgrade_flutter/app_upgrade_simple.dart'; void main() { group('parseUpdateContent', () { test('extracts header block and preserves nested markers', () { const content = ''' /{ 本次 **V2.2.0** 版本聚焦[使用体验]优化。 __支持嵌套__,例如 **[特别注意]** 与 `version 2.0` /} 1. 第一条说明 2. 第二条说明 '''; final parsed = AppUpgradeSimple.parseUpdateContent(content); expect(parsed.hasExplicitHeaderBlock, isTrue); expect( parsed.header, '本次 **V2.2.0** 版本聚焦[使用体验]优化。\n__支持嵌套__,例如 **[特别注意]** 与 `version 2.0`', ); expect(parsed.bodyItems, hasLength(2)); expect(parsed.bodyItems.first.text, '1. 第一条说明'); expect(parsed.bodyItems.first.hasLeadingMarker, isTrue); expect(parsed.bodyItems.last.text, '2. 第二条说明'); }); test('supports inline header block', () { const content = ''' /{ 更新内容如下: /} - 第一条说明 普通说明 '''; final parsed = AppUpgradeSimple.parseUpdateContent(content); expect(parsed.hasExplicitHeaderBlock, isTrue); expect(parsed.header, '更新内容如下:'); expect(parsed.bodyItems, hasLength(2)); expect(parsed.bodyItems.first.hasLeadingMarker, isTrue); expect(parsed.bodyItems.last.hasLeadingMarker, isFalse); }); test('supports explicit but empty header block', () { const content = ''' /{ /} 正文说明 '''; final parsed = AppUpgradeSimple.parseUpdateContent(content); expect(parsed.hasExplicitHeaderBlock, isTrue); expect(parsed.header, isNull); expect(parsed.bodyItems, hasLength(1)); expect(parsed.bodyItems.first.text, '正文说明'); }); test('keeps original content when header block is missing', () { const content = ''' **重要内容** 第二行说明 '''; final parsed = AppUpgradeSimple.parseUpdateContent(content); expect(parsed.hasExplicitHeaderBlock, isFalse); expect(parsed.header, isNull); expect(parsed.bodyItems, hasLength(2)); expect(parsed.bodyItems.first.text, '**重要内容**'); expect(parsed.bodyItems.last.text, '第二行说明'); }); test('preserves blank lines inside header block', () { const content = ''' /{ 第一段说明 第二段说明 /} 正文说明 '''; final parsed = AppUpgradeSimple.parseUpdateContent(content); expect(parsed.hasExplicitHeaderBlock, isTrue); expect(parsed.header, '第一段说明\n\n第二段说明'); expect(parsed.bodyItems, hasLength(1)); expect(parsed.bodyItems.first.text, '正文说明'); }); test('falls back to original content when header block is not closed', () { const content = ''' /{ 第一段说明 正文说明 '''; final parsed = AppUpgradeSimple.parseUpdateContent(content); expect(parsed.hasExplicitHeaderBlock, isFalse); expect(parsed.header, isNull); expect(parsed.bodyItems, hasLength(3)); expect(parsed.bodyItems[0].text, '/{'); expect(parsed.bodyItems[1].text, '第一段说明'); expect(parsed.bodyItems[2].text, '正文说明'); }); test('supports trailing body text on the same line as header end marker', () { const content = ''' /{ 头部说明 /} 1. 第一条说明 '''; final parsed = AppUpgradeSimple.parseUpdateContent(content); expect(parsed.hasExplicitHeaderBlock, isTrue); expect(parsed.header, '头部说明'); expect(parsed.bodyItems, hasLength(1)); expect(parsed.bodyItems.first.text, '1. 第一条说明'); expect(parsed.bodyItems.first.hasLeadingMarker, isTrue); }); test('normalizes windows line endings', () { const content = '/{\r\n头部说明\r\n/}\r\n- 第一条\r\n普通说明\r\n'; final parsed = AppUpgradeSimple.parseUpdateContent(content); expect(parsed.hasExplicitHeaderBlock, isTrue); expect(parsed.header, '头部说明'); expect(parsed.bodyItems, hasLength(2)); expect(parsed.bodyItems.first.text, '- 第一条'); expect(parsed.bodyItems.last.text, '普通说明'); }); test('detects multiple supported leading marker styles', () { const content = ''' 1. 数字序号 一、中文序号 - 无序列表 • 特殊符号列表 普通段落 '''; final parsed = AppUpgradeSimple.parseUpdateContent(content); expect(parsed.hasExplicitHeaderBlock, isFalse); expect(parsed.bodyItems, hasLength(5)); expect(parsed.bodyItems[0].hasLeadingMarker, isTrue); expect(parsed.bodyItems[1].hasLeadingMarker, isTrue); expect(parsed.bodyItems[2].hasLeadingMarker, isTrue); expect(parsed.bodyItems[3].hasLeadingMarker, isTrue); expect(parsed.bodyItems[4].hasLeadingMarker, isFalse); }); test('does not treat later header markers inside body as header block', () { const content = ''' 普通说明 /{ 这不是头部 /} 第二行正文 '''; final parsed = AppUpgradeSimple.parseUpdateContent(content); expect(parsed.hasExplicitHeaderBlock, isFalse); expect(parsed.header, isNull); expect(parsed.bodyItems, hasLength(3)); expect(parsed.bodyItems[1].text, '/{ 这不是头部 /}'); }); test('returns empty result for blank content', () { final parsed = AppUpgradeSimple.parseUpdateContent(' \n \r\n '); expect(parsed.hasExplicitHeaderBlock, isFalse); expect(parsed.header, isNull); expect(parsed.bodyItems, isEmpty); }); test('keeps the final growth-center line as a separate body item', () { const content = ''' /{本次 **V2.2.0** 版本围绕[使用体验]、[账号安全]、[家校联动]、[学习管理]等方面进行多项优化与功能新增,具体更新内容如下:/} 工作录入新增**草稿箱功能**,录入内容自动暂存,录入途中临时退出、意外关闭页面,都不会丢失填写数据,有效避免重复录入,提升使用效率; 优化**多账号登录机制**,若同一手机号绑定多个账号,登录时需手动选择本次要登录的对应账号,区分账号使用,保障使用精准性; 全新上线**家校互动板块**,新增家长会、社群运营、线上家访特色功能,搭建高效家校沟通渠道,助力家校协同共育; **成长中心**全面调整优化:成长中心入口迁移至工作台页面,升级全局搜索能力,章节内容改为结构化排版展示;优化视频播放逻辑与播放限制规则,同时新增学习过程监管功能,强化学习管控。 '''; final parsed = AppUpgradeSimple.parseUpdateContent(content); expect(parsed.hasExplicitHeaderBlock, isTrue); expect( parsed.header, '本次 **V2.2.0** 版本围绕[使用体验]、[账号安全]、[家校联动]、[学习管理]等方面进行多项优化与功能新增,具体更新内容如下:', ); expect(parsed.bodyItems, hasLength(4)); expect(parsed.bodyItems[0].text, startsWith('工作录入新增**草稿箱功能**')); expect(parsed.bodyItems[1].text, startsWith('优化**多账号登录机制**')); expect(parsed.bodyItems[2].text, startsWith('全新上线**家校互动板块**')); expect( parsed.bodyItems[3].text, '**成长中心**全面调整优化:成长中心入口迁移至工作台页面,升级全局搜索能力,章节内容改为结构化排版展示;优化视频播放逻辑与播放限制规则,同时新增学习过程监管功能,强化学习管控。', ); }); test('does not treat bold marker at line start as list marker', () { const content = ''' **成长中心**全面调整优化:成长中心入口迁移至工作台页面,升级全局搜索能力。 * 合法无序列表项 '''; final parsed = AppUpgradeSimple.parseUpdateContent(content); expect(parsed.bodyItems, hasLength(2)); expect(parsed.bodyItems.first.text, '**成长中心**全面调整优化:成长中心入口迁移至工作台页面,升级全局搜索能力。'); expect(parsed.bodyItems.first.hasLeadingMarker, isFalse); expect(parsed.bodyItems.last.text, '* 合法无序列表项'); expect(parsed.bodyItems.last.hasLeadingMarker, isTrue); }); test( 'supports all rich text markers when they appear on the first body line', () { const content = ''' **粗体首行** __斜体次行__ `代码第三行` [高亮第四行] **[组合重点]** '''; final parsed = AppUpgradeSimple.parseUpdateContent(content); expect(parsed.hasExplicitHeaderBlock, isFalse); expect(parsed.bodyItems, hasLength(5)); expect(parsed.bodyItems[0].text, '**粗体首行**'); expect(parsed.bodyItems[1].text, '__斜体次行__'); expect(parsed.bodyItems[2].text, '`代码第三行`'); expect(parsed.bodyItems[3].text, '[高亮第四行]'); expect(parsed.bodyItems[4].text, '**[组合重点]**'); expect( parsed.bodyItems.every((item) => item.hasLeadingMarker == false), isTrue, ); }); test('supports rich text markers on the first line inside header block', () { const content = ''' /{ **头部粗体** __[头部高亮斜体]__ `头部代码` /} 正文说明 '''; final parsed = AppUpgradeSimple.parseUpdateContent(content); expect(parsed.hasExplicitHeaderBlock, isTrue); expect( parsed.header, '**头部粗体**\n__[头部高亮斜体]__\n`头部代码`', ); expect(parsed.bodyItems, hasLength(1)); expect(parsed.bodyItems.first.text, '正文说明'); }); test('does not treat version-like prefix as ordered marker', () { const content = ''' 1.0版本修复说明 2. 第一条真正的序号内容 '''; final parsed = AppUpgradeSimple.parseUpdateContent(content); expect(parsed.bodyItems, hasLength(2)); expect(parsed.bodyItems.first.text, '1.0版本修复说明'); expect(parsed.bodyItems.first.hasLeadingMarker, isFalse); expect(parsed.bodyItems.last.text, '2. 第一条真正的序号内容'); expect(parsed.bodyItems.last.hasLeadingMarker, isTrue); }); }); }