第一段

第二段
续行

'; $text = StudyTourDeclarationExporter::htmlToPlainText($html); $this->assertStringContainsString("第一段", $text); $this->assertStringContainsString("第二段", $text); $this->assertStringContainsString("续行", $text); $this->assertStringContainsString("要点A", $text); $this->assertStringContainsString("要点B", $text); } public function test_sanitize_filename_strips_illegal_chars(): void { $this->assertSame('线路A_B', StudyTourDeclarationExporter::sanitizeFilename('线路A/B')); $this->assertSame('研学线路', StudyTourDeclarationExporter::sanitizeFilename(' ')); } public function test_docx_contains_declaration_sections_and_cover_image(): void { $png = sys_get_temp_dir().'/st-cover-test.png'; file_put_contents($png, base64_decode( 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg==' )); $path = StudyTourDeclarationExporter::writeDocxFile([ 'name' => '太湖蚕桑研学', 'org_name' => '苏州市科协', 'seasons' => ['spring', 'autumn'], 'venue_names' => ['苏州丝绸博物馆', '自定义营地'], 'suitable_count' => '30人', 'suitable_audience' => 'primary,junior', 'duration' => '2天1晚', 'contact_person' => '张老师', 'contact_phones' => '13800000000', 'cover_image_path' => $png, 'intro_html' => '

走进蚕桑文化。

', 'route_plans' => [[ 'date_label' => '第一天', 'items' => [[ 'time' => '09:00', 'activity' => '开营仪式', 'location' => '活动中心', ]], ]], 'courses' => [[ 'sort' => 1, 'name' => '缫丝体验', 'content' => '了解传统缫丝工艺', ]], 'fee_html' => '

680元/人

', 'implementation_html' => '

2026年7月1日第1场

', 'season_options' => [ ['value' => 'spring', 'label' => '春季'], ['value' => 'summer', 'label' => '夏季'], ['value' => 'autumn', 'label' => '秋季'], ['value' => 'winter', 'label' => '冬季'], ], 'audience_options' => [ ['value' => 'kindergarten', 'label' => '幼儿园'], ['value' => 'primary', 'label' => '小学'], ['value' => 'junior', 'label' => '初中'], ['value' => 'high', 'label' => '高中'], ['value' => 'all', 'label' => '全学段'], ], ]); $this->assertFileExists($path); $zip = new ZipArchive(); $this->assertTrue($zip->open($path) === true); $xml = (string) $zip->getFromName('word/document.xml'); $mediaCount = 0; for ($i = 0; $i < $zip->numFiles; $i++) { $name = (string) $zip->getNameIndex($i); if (str_starts_with($name, 'word/media/')) { $mediaCount++; } } $zip->close(); foreach ([ '一、线路基本情况', '二、线路简介', '三、线路规划', '四、研学课程', '五、线路收费标准', '六、线路计划实施情况', '太湖蚕桑研学', '苏州市科协', '苏州丝绸博物馆', '走进蚕桑文化', '开营仪式', '缫丝体验', '680元/人', '封面图', ] as $needle) { $this->assertStringContainsString($needle, $xml, "missing {$needle}"); } $this->assertGreaterThan(0, $mediaCount, 'cover image should be embedded'); $phpWord = IOFactory::load($path); $this->assertNotEmpty($phpWord->getSections()); @unlink($path); @unlink($png); } }