mirror of
https://github.com/kjanat/articulate-parser.git
synced 2026-01-16 09:02:10 +01:00
Add comprehensive unit tests for services and main package
- Implement tests for the app service, including course processing from file and URI. - Create mock implementations for CourseParser and Exporter to facilitate testing. - Add tests for HTML cleaner service to validate HTML content cleaning functionality. - Develop tests for the parser service, covering course fetching and loading from files. - Introduce tests for utility functions in the main package, ensuring URI validation and string joining. - Include benchmarks for performance evaluation of key functions.
This commit is contained in:
@ -4,17 +4,18 @@ package exporters
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/fumiama/go-docx"
|
||||
"github.com/kjanat/articulate-parser/internal/interfaces"
|
||||
"github.com/kjanat/articulate-parser/internal/models"
|
||||
"github.com/kjanat/articulate-parser/internal/services"
|
||||
"github.com/unidoc/unioffice/document"
|
||||
)
|
||||
|
||||
// DocxExporter implements the Exporter interface for DOCX format.
|
||||
// It converts Articulate Rise course data into a Microsoft Word document
|
||||
// using the unioffice/document package.
|
||||
// using the go-docx package.
|
||||
type DocxExporter struct {
|
||||
// htmlCleaner is used to convert HTML content to plain text
|
||||
htmlCleaner *services.HTMLCleaner
|
||||
@ -45,21 +46,17 @@ func NewDocxExporter(htmlCleaner *services.HTMLCleaner) interfaces.Exporter {
|
||||
// Returns:
|
||||
// - An error if creating or saving the document fails
|
||||
func (e *DocxExporter) Export(course *models.Course, outputPath string) error {
|
||||
doc := document.New()
|
||||
doc := docx.New()
|
||||
|
||||
// Add title
|
||||
titlePara := doc.AddParagraph()
|
||||
titleRun := titlePara.AddRun()
|
||||
titleRun.AddText(course.Course.Title)
|
||||
titleRun.Properties().SetBold(true)
|
||||
titleRun.Properties().SetSize(16)
|
||||
titlePara.AddText(course.Course.Title).Size("32").Bold()
|
||||
|
||||
// Add description if available
|
||||
if course.Course.Description != "" {
|
||||
descPara := doc.AddParagraph()
|
||||
descRun := descPara.AddRun()
|
||||
cleanDesc := e.htmlCleaner.CleanHTML(course.Course.Description)
|
||||
descRun.AddText(cleanDesc)
|
||||
descPara.AddText(cleanDesc)
|
||||
}
|
||||
|
||||
// Add each lesson
|
||||
@ -72,7 +69,20 @@ func (e *DocxExporter) Export(course *models.Course, outputPath string) error {
|
||||
outputPath = outputPath + ".docx"
|
||||
}
|
||||
|
||||
return doc.SaveToFile(outputPath)
|
||||
// Create the file
|
||||
file, err := os.Create(outputPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create output file: %w", err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
// Save the document
|
||||
_, err = doc.WriteTo(file)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to save document: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// exportLesson adds a lesson to the document with appropriate formatting.
|
||||
@ -81,20 +91,16 @@ func (e *DocxExporter) Export(course *models.Course, outputPath string) error {
|
||||
// Parameters:
|
||||
// - doc: The Word document being created
|
||||
// - lesson: The lesson data model to export
|
||||
func (e *DocxExporter) exportLesson(doc *document.Document, lesson *models.Lesson) {
|
||||
func (e *DocxExporter) exportLesson(doc *docx.Docx, lesson *models.Lesson) {
|
||||
// Add lesson title
|
||||
lessonPara := doc.AddParagraph()
|
||||
lessonRun := lessonPara.AddRun()
|
||||
lessonRun.AddText(fmt.Sprintf("Lesson: %s", lesson.Title))
|
||||
lessonRun.Properties().SetBold(true)
|
||||
lessonRun.Properties().SetSize(14)
|
||||
lessonPara.AddText(fmt.Sprintf("Lesson: %s", lesson.Title)).Size("28").Bold()
|
||||
|
||||
// Add lesson description if available
|
||||
if lesson.Description != "" {
|
||||
descPara := doc.AddParagraph()
|
||||
descRun := descPara.AddRun()
|
||||
cleanDesc := e.htmlCleaner.CleanHTML(lesson.Description)
|
||||
descRun.AddText(cleanDesc)
|
||||
descPara.AddText(cleanDesc)
|
||||
}
|
||||
|
||||
// Add each item in the lesson
|
||||
@ -109,14 +115,11 @@ func (e *DocxExporter) exportLesson(doc *document.Document, lesson *models.Lesso
|
||||
// Parameters:
|
||||
// - doc: The Word document being created
|
||||
// - item: The item data model to export
|
||||
func (e *DocxExporter) exportItem(doc *document.Document, item *models.Item) {
|
||||
func (e *DocxExporter) exportItem(doc *docx.Docx, item *models.Item) {
|
||||
// Add item type as heading
|
||||
if item.Type != "" {
|
||||
itemPara := doc.AddParagraph()
|
||||
itemRun := itemPara.AddRun()
|
||||
itemRun.AddText(strings.Title(item.Type))
|
||||
itemRun.Properties().SetBold(true)
|
||||
itemRun.Properties().SetSize(12)
|
||||
itemPara.AddText(strings.Title(item.Type)).Size("24").Bold()
|
||||
}
|
||||
|
||||
// Add sub-items
|
||||
@ -132,58 +135,48 @@ func (e *DocxExporter) exportItem(doc *document.Document, item *models.Item) {
|
||||
// Parameters:
|
||||
// - doc: The Word document being created
|
||||
// - subItem: The sub-item data model to export
|
||||
func (e *DocxExporter) exportSubItem(doc *document.Document, subItem *models.SubItem) {
|
||||
func (e *DocxExporter) exportSubItem(doc *docx.Docx, subItem *models.SubItem) {
|
||||
// Add title if available
|
||||
if subItem.Title != "" {
|
||||
subItemPara := doc.AddParagraph()
|
||||
subItemRun := subItemPara.AddRun()
|
||||
subItemRun.AddText(" " + subItem.Title) // Indented
|
||||
subItemRun.Properties().SetBold(true)
|
||||
subItemPara.AddText(" " + subItem.Title).Bold() // Indented
|
||||
}
|
||||
|
||||
// Add heading if available
|
||||
if subItem.Heading != "" {
|
||||
headingPara := doc.AddParagraph()
|
||||
headingRun := headingPara.AddRun()
|
||||
cleanHeading := e.htmlCleaner.CleanHTML(subItem.Heading)
|
||||
headingRun.AddText(" " + cleanHeading) // Indented
|
||||
headingRun.Properties().SetBold(true)
|
||||
headingPara.AddText(" " + cleanHeading).Bold() // Indented
|
||||
}
|
||||
|
||||
// Add paragraph content if available
|
||||
if subItem.Paragraph != "" {
|
||||
contentPara := doc.AddParagraph()
|
||||
contentRun := contentPara.AddRun()
|
||||
cleanContent := e.htmlCleaner.CleanHTML(subItem.Paragraph)
|
||||
contentRun.AddText(" " + cleanContent) // Indented
|
||||
contentPara.AddText(" " + cleanContent) // Indented
|
||||
}
|
||||
|
||||
// Add answers if this is a question
|
||||
if len(subItem.Answers) > 0 {
|
||||
answersPara := doc.AddParagraph()
|
||||
answersRun := answersPara.AddRun()
|
||||
answersRun.AddText(" Answers:")
|
||||
answersRun.Properties().SetBold(true)
|
||||
answersPara.AddText(" Answers:").Bold()
|
||||
|
||||
for i, answer := range subItem.Answers {
|
||||
answerPara := doc.AddParagraph()
|
||||
answerRun := answerPara.AddRun()
|
||||
prefix := fmt.Sprintf(" %d. ", i+1)
|
||||
if answer.Correct {
|
||||
prefix += "✓ "
|
||||
}
|
||||
cleanAnswer := e.htmlCleaner.CleanHTML(answer.Title)
|
||||
answerRun.AddText(prefix + cleanAnswer)
|
||||
answerPara.AddText(prefix + cleanAnswer)
|
||||
}
|
||||
}
|
||||
|
||||
// Add feedback if available
|
||||
if subItem.Feedback != "" {
|
||||
feedbackPara := doc.AddParagraph()
|
||||
feedbackRun := feedbackPara.AddRun()
|
||||
cleanFeedback := e.htmlCleaner.CleanHTML(subItem.Feedback)
|
||||
feedbackRun.AddText(" Feedback: " + cleanFeedback)
|
||||
feedbackRun.Properties().SetItalic(true)
|
||||
feedbackPara.AddText(" Feedback: " + cleanFeedback).Italic()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user