Nudge-Screen
This commit is contained in:
@@ -0,0 +1,244 @@
|
||||
import Testing
|
||||
@testable import bookstax
|
||||
import Foundation
|
||||
|
||||
// MARK: - Helpers
|
||||
|
||||
private func makePageDTO(markdown: String? = "# Hello", tags: [TagDTO] = []) -> PageDTO {
|
||||
PageDTO(
|
||||
id: 42,
|
||||
bookId: 1,
|
||||
chapterId: nil,
|
||||
name: "Test Page",
|
||||
slug: "test-page",
|
||||
html: nil,
|
||||
markdown: markdown,
|
||||
priority: 0,
|
||||
draftStatus: false,
|
||||
tags: tags,
|
||||
createdAt: Date(),
|
||||
updatedAt: Date()
|
||||
)
|
||||
}
|
||||
|
||||
// MARK: - Initialisation
|
||||
|
||||
@Suite("PageEditorViewModel – Initialisation")
|
||||
struct PageEditorViewModelInitTests {
|
||||
|
||||
@Test("Create mode starts with empty title and content")
|
||||
func createModeDefaults() {
|
||||
let vm = PageEditorViewModel(mode: .create(bookId: 1))
|
||||
#expect(vm.title.isEmpty)
|
||||
#expect(vm.markdownContent.isEmpty)
|
||||
#expect(vm.tags.isEmpty)
|
||||
#expect(vm.isHtmlOnlyPage == false)
|
||||
}
|
||||
|
||||
@Test("Edit mode populates title and markdown from page")
|
||||
func editModePopulates() {
|
||||
let page = makePageDTO(markdown: "## Content")
|
||||
let vm = PageEditorViewModel(mode: .edit(page: page))
|
||||
#expect(vm.title == "Test Page")
|
||||
#expect(vm.markdownContent == "## Content")
|
||||
#expect(vm.isHtmlOnlyPage == false)
|
||||
}
|
||||
|
||||
@Test("Edit mode with nil markdown sets isHtmlOnlyPage")
|
||||
func htmlOnlyPage() {
|
||||
let page = makePageDTO(markdown: nil)
|
||||
let vm = PageEditorViewModel(mode: .edit(page: page))
|
||||
#expect(vm.isHtmlOnlyPage == true)
|
||||
#expect(vm.markdownContent.isEmpty)
|
||||
}
|
||||
|
||||
@Test("Edit mode with existing tags loads them")
|
||||
func editModeLoadsTags() {
|
||||
let tags = [TagDTO(name: "topic", value: "swift", order: 0)]
|
||||
let page = makePageDTO(tags: tags)
|
||||
let vm = PageEditorViewModel(mode: .edit(page: page))
|
||||
#expect(vm.tags.count == 1)
|
||||
#expect(vm.tags.first?.name == "topic")
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - hasUnsavedChanges
|
||||
|
||||
@Suite("PageEditorViewModel – hasUnsavedChanges")
|
||||
struct PageEditorViewModelUnsavedTests {
|
||||
|
||||
@Test("No changes after init → hasUnsavedChanges is false")
|
||||
func noChangesAfterInit() {
|
||||
let vm = PageEditorViewModel(mode: .create(bookId: 1))
|
||||
#expect(vm.hasUnsavedChanges == false)
|
||||
}
|
||||
|
||||
@Test("Changing title → hasUnsavedChanges is true")
|
||||
func titleChange() {
|
||||
let vm = PageEditorViewModel(mode: .create(bookId: 1))
|
||||
vm.title = "New Title"
|
||||
#expect(vm.hasUnsavedChanges == true)
|
||||
}
|
||||
|
||||
@Test("Changing markdownContent → hasUnsavedChanges is true")
|
||||
func contentChange() {
|
||||
let vm = PageEditorViewModel(mode: .create(bookId: 1))
|
||||
vm.markdownContent = "Some text"
|
||||
#expect(vm.hasUnsavedChanges == true)
|
||||
}
|
||||
|
||||
@Test("Adding a tag → hasUnsavedChanges is true")
|
||||
func tagAddition() {
|
||||
let page = makePageDTO()
|
||||
let vm = PageEditorViewModel(mode: .edit(page: page))
|
||||
vm.addTag(name: "new-tag")
|
||||
#expect(vm.hasUnsavedChanges == true)
|
||||
}
|
||||
|
||||
@Test("Restoring original values → hasUnsavedChanges is false again")
|
||||
func revertChanges() {
|
||||
let vm = PageEditorViewModel(mode: .create(bookId: 1))
|
||||
vm.title = "Changed"
|
||||
vm.title = ""
|
||||
#expect(vm.hasUnsavedChanges == false)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - isSaveDisabled
|
||||
|
||||
@Suite("PageEditorViewModel – isSaveDisabled")
|
||||
struct PageEditorViewModelSaveDisabledTests {
|
||||
|
||||
@Test("Empty title disables save in create mode")
|
||||
func emptyTitleCreate() {
|
||||
let vm = PageEditorViewModel(mode: .create(bookId: 1))
|
||||
vm.markdownContent = "Some content"
|
||||
#expect(vm.isSaveDisabled == true)
|
||||
}
|
||||
|
||||
@Test("Empty content disables save in create mode even if title is set")
|
||||
func emptyContentCreate() {
|
||||
let vm = PageEditorViewModel(mode: .create(bookId: 1))
|
||||
vm.title = "My Page"
|
||||
vm.markdownContent = ""
|
||||
#expect(vm.isSaveDisabled == true)
|
||||
}
|
||||
|
||||
@Test("Whitespace-only content disables save in create mode")
|
||||
func whitespaceContentCreate() {
|
||||
let vm = PageEditorViewModel(mode: .create(bookId: 1))
|
||||
vm.title = "My Page"
|
||||
vm.markdownContent = " \n "
|
||||
#expect(vm.isSaveDisabled == true)
|
||||
}
|
||||
|
||||
@Test("Title and content both set enables save in create mode")
|
||||
func validCreate() {
|
||||
let vm = PageEditorViewModel(mode: .create(bookId: 1))
|
||||
vm.title = "My Page"
|
||||
vm.markdownContent = "Hello world"
|
||||
#expect(vm.isSaveDisabled == false)
|
||||
}
|
||||
|
||||
@Test("Edit mode only requires title – empty content is allowed")
|
||||
func editOnlyNeedsTitle() {
|
||||
let page = makePageDTO(markdown: nil)
|
||||
let vm = PageEditorViewModel(mode: .edit(page: page))
|
||||
vm.title = "Existing Page"
|
||||
vm.markdownContent = ""
|
||||
#expect(vm.isSaveDisabled == false)
|
||||
}
|
||||
|
||||
@Test("Empty title disables save in edit mode")
|
||||
func emptyTitleEdit() {
|
||||
let page = makePageDTO()
|
||||
let vm = PageEditorViewModel(mode: .edit(page: page))
|
||||
vm.title = ""
|
||||
#expect(vm.isSaveDisabled == true)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Tag Management
|
||||
|
||||
@Suite("PageEditorViewModel – Tags")
|
||||
struct PageEditorViewModelTagTests {
|
||||
|
||||
@Test("addTag appends a new tag")
|
||||
func addTag() {
|
||||
let vm = PageEditorViewModel(mode: .create(bookId: 1))
|
||||
vm.addTag(name: "status", value: "draft")
|
||||
#expect(vm.tags.count == 1)
|
||||
#expect(vm.tags.first?.name == "status")
|
||||
#expect(vm.tags.first?.value == "draft")
|
||||
}
|
||||
|
||||
@Test("addTag trims whitespace from name and value")
|
||||
func addTagTrims() {
|
||||
let vm = PageEditorViewModel(mode: .create(bookId: 1))
|
||||
vm.addTag(name: " topic ", value: " swift ")
|
||||
#expect(vm.tags.first?.name == "topic")
|
||||
#expect(vm.tags.first?.value == "swift")
|
||||
}
|
||||
|
||||
@Test("addTag with empty name after trimming is ignored")
|
||||
func addTagEmptyNameIgnored() {
|
||||
let vm = PageEditorViewModel(mode: .create(bookId: 1))
|
||||
vm.addTag(name: " ")
|
||||
#expect(vm.tags.isEmpty)
|
||||
}
|
||||
|
||||
@Test("addTag prevents duplicate (same name + value) entries")
|
||||
func addTagNoDuplicates() {
|
||||
let vm = PageEditorViewModel(mode: .create(bookId: 1))
|
||||
vm.addTag(name: "lang", value: "swift")
|
||||
vm.addTag(name: "lang", value: "swift")
|
||||
#expect(vm.tags.count == 1)
|
||||
}
|
||||
|
||||
@Test("addTag allows same name with different value")
|
||||
func addTagSameNameDifferentValue() {
|
||||
let vm = PageEditorViewModel(mode: .create(bookId: 1))
|
||||
vm.addTag(name: "env", value: "dev")
|
||||
vm.addTag(name: "env", value: "prod")
|
||||
#expect(vm.tags.count == 2)
|
||||
}
|
||||
|
||||
@Test("removeTag removes the matching tag by id")
|
||||
func removeTag() {
|
||||
let vm = PageEditorViewModel(mode: .create(bookId: 1))
|
||||
vm.addTag(name: "remove-me", value: "yes")
|
||||
let tag = vm.tags[0]
|
||||
vm.removeTag(tag)
|
||||
#expect(vm.tags.isEmpty)
|
||||
}
|
||||
|
||||
@Test("removeTag does not remove non-matching tags")
|
||||
func removeTagKeepsOthers() {
|
||||
let vm = PageEditorViewModel(mode: .create(bookId: 1))
|
||||
vm.addTag(name: "keep", value: "")
|
||||
vm.addTag(name: "remove", value: "")
|
||||
let toRemove = vm.tags.first { $0.name == "remove" }!
|
||||
vm.removeTag(toRemove)
|
||||
#expect(vm.tags.count == 1)
|
||||
#expect(vm.tags.first?.name == "keep")
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - uploadTargetPageId
|
||||
|
||||
@Suite("PageEditorViewModel – uploadTargetPageId")
|
||||
struct PageEditorViewModelUploadIDTests {
|
||||
|
||||
@Test("Create mode returns 0 as upload target")
|
||||
func createModeUploadTarget() {
|
||||
let vm = PageEditorViewModel(mode: .create(bookId: 5))
|
||||
#expect(vm.uploadTargetPageId == 0)
|
||||
}
|
||||
|
||||
@Test("Edit mode returns the existing page id")
|
||||
func editModeUploadTarget() {
|
||||
let page = makePageDTO()
|
||||
let vm = PageEditorViewModel(mode: .edit(page: page))
|
||||
#expect(vm.uploadTargetPageId == 42)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user