Prepare v0.4 release and open source docs
This commit is contained in:
52
Tests/IHatePDFsCoreTests/PDFDocumentBookmarksTests.swift
Normal file
52
Tests/IHatePDFsCoreTests/PDFDocumentBookmarksTests.swift
Normal file
@@ -0,0 +1,52 @@
|
||||
import XCTest
|
||||
@testable import IHatePDFsCore
|
||||
|
||||
final class PDFDocumentBookmarksTests: XCTestCase {
|
||||
func testUpsertReplacesExistingBookmark() {
|
||||
let first = PDFDocumentBookmark(id: "first", pageIndex: 4, pageLabel: "5", title: "Old")
|
||||
let second = PDFDocumentBookmark(id: "second", pageIndex: 1, pageLabel: "2", title: "Second")
|
||||
let replacement = PDFDocumentBookmark(id: "replacement", pageIndex: 4, pageLabel: "5", title: "New")
|
||||
|
||||
let result = PDFDocumentBookmarks.upsert(replacement, in: [first, second])
|
||||
|
||||
XCTAssertEqual(result.map(\.id), ["replacement"])
|
||||
XCTAssertEqual(result.first?.title, "New")
|
||||
}
|
||||
|
||||
func testRemovingBookmarkCollapsesDirtyMultipleBookmarkData() {
|
||||
let first = PDFDocumentBookmark(id: "first", pageIndex: 0, pageLabel: "1", title: "First")
|
||||
let second = PDFDocumentBookmark(id: "second", pageIndex: 1, pageLabel: "2", title: "Second")
|
||||
|
||||
let result = PDFDocumentBookmarks.removing(id: "first", from: [first, second])
|
||||
|
||||
XCTAssertEqual(result.map(\.id), ["second"])
|
||||
}
|
||||
|
||||
func testClampedDropsInvalidBookmarksAndKeepsOneBookmark() {
|
||||
let older = PDFDocumentBookmark(
|
||||
id: "older",
|
||||
pageIndex: 0,
|
||||
pageLabel: "1",
|
||||
title: "Older",
|
||||
createdAt: Date(timeIntervalSince1970: 100)
|
||||
)
|
||||
let newer = PDFDocumentBookmark(
|
||||
id: "newer",
|
||||
pageIndex: 1,
|
||||
pageLabel: "2",
|
||||
title: "Newer",
|
||||
createdAt: Date(timeIntervalSince1970: 200)
|
||||
)
|
||||
let invalid = PDFDocumentBookmark(
|
||||
id: "invalid",
|
||||
pageIndex: 4,
|
||||
pageLabel: "5",
|
||||
title: "Invalid",
|
||||
createdAt: Date(timeIntervalSince1970: 300)
|
||||
)
|
||||
|
||||
let result = PDFDocumentBookmarks.clamped([invalid, older, newer], pageCount: 2)
|
||||
|
||||
XCTAssertEqual(result.map(\.id), ["newer"])
|
||||
}
|
||||
}
|
||||
58
Tests/IHatePDFsCoreTests/PDFRecentDocumentsTests.swift
Normal file
58
Tests/IHatePDFsCoreTests/PDFRecentDocumentsTests.swift
Normal file
@@ -0,0 +1,58 @@
|
||||
import XCTest
|
||||
@testable import IHatePDFsCore
|
||||
|
||||
final class PDFRecentDocumentsTests: XCTestCase {
|
||||
func testFilteredPDFsKeepsExistingPDFsOnly() {
|
||||
let pdf = URL(fileURLWithPath: "/Users/test/Documents/reading.pdf")
|
||||
let upperPDF = URL(fileURLWithPath: "/Users/test/Documents/report.PDF")
|
||||
let text = URL(fileURLWithPath: "/Users/test/Documents/notes.txt")
|
||||
|
||||
let result = PDFRecentDocuments.filteredPDFs(
|
||||
from: [pdf, text, upperPDF],
|
||||
limit: 10,
|
||||
fileExists: { $0 == pdf || $0 == upperPDF }
|
||||
)
|
||||
|
||||
XCTAssertEqual(result, [pdf, upperPDF])
|
||||
}
|
||||
|
||||
func testFilteredPDFsDeduplicatesExcludesCurrentAndHonorsLimit() {
|
||||
let first = URL(fileURLWithPath: "/Users/test/Documents/first.pdf")
|
||||
let second = URL(fileURLWithPath: "/Users/test/Documents/second.pdf")
|
||||
let third = URL(fileURLWithPath: "/Users/test/Documents/third.pdf")
|
||||
|
||||
let result = PDFRecentDocuments.filteredPDFs(
|
||||
from: [first, second, first, third],
|
||||
currentURL: second,
|
||||
limit: 1,
|
||||
fileExists: { _ in true }
|
||||
)
|
||||
|
||||
XCTAssertEqual(result, [first])
|
||||
}
|
||||
|
||||
func testProgressStoresPageByNormalizedDocumentKey() {
|
||||
let url = URL(fileURLWithPath: "/Users/test/Documents/../Documents/reading.pdf")
|
||||
let openedAt = Date(timeIntervalSince1970: 42)
|
||||
|
||||
let records = PDFRecentDocuments.updatedProgress(
|
||||
[:],
|
||||
url: url,
|
||||
pageIndex: 8,
|
||||
openedAt: openedAt
|
||||
)
|
||||
let progress = PDFRecentDocuments.progress(for: url, in: records)
|
||||
|
||||
XCTAssertEqual(progress?.pageIndex, 8)
|
||||
XCTAssertEqual(progress?.openedAt, openedAt)
|
||||
XCTAssertEqual(progress?.key, PDFRecentDocuments.documentKey(for: url))
|
||||
}
|
||||
|
||||
func testProgressClampsSavedPageToAvailablePageCount() {
|
||||
XCTAssertEqual(PDFRecentDocuments.clampedPageIndex(nil, pageCount: 20), 0)
|
||||
XCTAssertEqual(PDFRecentDocuments.clampedPageIndex(-4, pageCount: 20), 0)
|
||||
XCTAssertEqual(PDFRecentDocuments.clampedPageIndex(4, pageCount: 20), 4)
|
||||
XCTAssertEqual(PDFRecentDocuments.clampedPageIndex(99, pageCount: 20), 19)
|
||||
XCTAssertEqual(PDFRecentDocuments.clampedPageIndex(4, pageCount: 0), 0)
|
||||
}
|
||||
}
|
||||
46
Tests/IHatePDFsCoreTests/PerformanceBudgetTests.swift
Normal file
46
Tests/IHatePDFsCoreTests/PerformanceBudgetTests.swift
Normal file
@@ -0,0 +1,46 @@
|
||||
import AppKit
|
||||
import PDFKit
|
||||
import XCTest
|
||||
@testable import IHatePDFsCore
|
||||
|
||||
final class PerformanceBudgetTests: XCTestCase {
|
||||
func testLargeDocumentFullAnnotationSnapshotPerformance() {
|
||||
let document = makeLargeAnnotatedDocument(pageCount: 500, annotationEvery: 10)
|
||||
|
||||
measure {
|
||||
let snapshots = AnnotationReader.snapshots(in: document)
|
||||
XCTAssertEqual(snapshots.count, 50)
|
||||
}
|
||||
}
|
||||
|
||||
func testLargeDocumentPageScopedAnnotationRefreshPerformance() throws {
|
||||
let document = makeLargeAnnotatedDocument(pageCount: 500, annotationEvery: 10)
|
||||
let targetPage = try XCTUnwrap(document.page(at: 250))
|
||||
|
||||
measure {
|
||||
let snapshots = AnnotationReader.snapshots(in: document, pages: [targetPage])
|
||||
XCTAssertEqual(snapshots.count, 1)
|
||||
XCTAssertEqual(snapshots.first?.pageIndex, 250)
|
||||
}
|
||||
}
|
||||
|
||||
private func makeLargeAnnotatedDocument(pageCount: Int, annotationEvery stride: Int) -> PDFDocument {
|
||||
let document = PDFDocument()
|
||||
|
||||
for pageIndex in 0..<pageCount {
|
||||
let page = PDFPage()
|
||||
document.insert(page, at: pageIndex)
|
||||
|
||||
guard pageIndex.isMultiple(of: stride) else { continue }
|
||||
let insertion = AnnotationFactory.noteInsertion(
|
||||
on: page,
|
||||
near: CGPoint(x: 120, y: 160),
|
||||
comment: "Note on page \(pageIndex + 1)",
|
||||
author: "Professor"
|
||||
)
|
||||
page.addAnnotation(insertion.annotation)
|
||||
}
|
||||
|
||||
return document
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,30 @@ final class ReturnKeyCommitPolicyTests: XCTestCase {
|
||||
))
|
||||
}
|
||||
|
||||
func testCommandReturnCommitsWhenCommandReturnOnlyModeEnabled() {
|
||||
XCTAssertTrue(ReturnKeyCommitPolicy.shouldCommit(
|
||||
keyCode: 36,
|
||||
shift: false,
|
||||
option: false,
|
||||
command: true,
|
||||
control: false,
|
||||
isEditableMultilineText: true,
|
||||
commandReturnOnly: true
|
||||
))
|
||||
}
|
||||
|
||||
func testPlainReturnDoesNotCommitWhenCommandReturnOnlyModeEnabled() {
|
||||
XCTAssertFalse(ReturnKeyCommitPolicy.shouldCommit(
|
||||
keyCode: 36,
|
||||
shift: false,
|
||||
option: false,
|
||||
command: false,
|
||||
control: false,
|
||||
isEditableMultilineText: true,
|
||||
commandReturnOnly: true
|
||||
))
|
||||
}
|
||||
|
||||
func testKeypadEnterCommitsInEditableMultilineText() {
|
||||
XCTAssertTrue(ReturnKeyCommitPolicy.shouldCommit(
|
||||
keyCode: 76,
|
||||
|
||||
290
Tests/IHatePDFsTests/AppStateWorkflowTests.swift
Normal file
290
Tests/IHatePDFsTests/AppStateWorkflowTests.swift
Normal file
@@ -0,0 +1,290 @@
|
||||
import CoreGraphics
|
||||
import Foundation
|
||||
import IHatePDFsCore
|
||||
import PDFKit
|
||||
import XCTest
|
||||
@testable import IHatePDFs
|
||||
|
||||
@MainActor
|
||||
final class AppStateWorkflowTests: XCTestCase {
|
||||
func testOpeningDocumentStartsInFocusedReadingWorkflow() throws {
|
||||
let url = try makeTemporaryPDF()
|
||||
defer { try? FileManager.default.removeItem(at: url) }
|
||||
|
||||
let appState = AppState()
|
||||
appState.updateWindowWidth(1_280)
|
||||
appState.showLeftSidebar = true
|
||||
appState.leftSidebarMode = .annotations
|
||||
appState.showCommentsSidebar = true
|
||||
appState.sidebarMode = .highlights
|
||||
appState.commentSearchText = "draft"
|
||||
appState.commentFilter = .withComments
|
||||
appState.selectedKindFilter = .comment
|
||||
appState.selectedAuthorFilter = "Someone"
|
||||
appState.selectedStatusFilter = ReviewState.reviewed
|
||||
appState.collapsedPageIndexes = [0]
|
||||
|
||||
appState.loadDocument(from: url)
|
||||
|
||||
XCTAssertNotNil(appState.document)
|
||||
XCTAssertEqual(appState.documentURL, url)
|
||||
XCTAssertFalse(appState.showLeftSidebar)
|
||||
XCTAssertEqual(appState.leftSidebarMode, .pages)
|
||||
XCTAssertFalse(appState.showCommentsSidebar)
|
||||
XCTAssertEqual(appState.sidebarMode, .annotations)
|
||||
XCTAssertEqual(appState.commentSearchText, "")
|
||||
XCTAssertEqual(appState.commentFilter, .all)
|
||||
XCTAssertNil(appState.selectedKindFilter)
|
||||
XCTAssertEqual(appState.selectedAuthorFilter, "All Authors")
|
||||
XCTAssertEqual(appState.selectedStatusFilter, ReviewState.allStatuses)
|
||||
XCTAssertTrue(appState.collapsedPageIndexes.isEmpty)
|
||||
XCTAssertFalse(appState.hasUnsavedChanges)
|
||||
}
|
||||
|
||||
func testOpeningDocumentCollapsesSidebars() throws {
|
||||
let url = try makeTemporaryPDF()
|
||||
defer { try? FileManager.default.removeItem(at: url) }
|
||||
|
||||
let appState = AppState()
|
||||
appState.updateWindowWidth(1_280)
|
||||
appState.showLeftSidebar = true
|
||||
appState.leftSidebarMode = .annotations
|
||||
appState.showCommentsSidebar = true
|
||||
appState.sidebarMode = .highlights
|
||||
|
||||
appState.loadDocument(from: url)
|
||||
|
||||
XCTAssertFalse(appState.showLeftSidebar)
|
||||
XCTAssertFalse(appState.showCommentsSidebar)
|
||||
}
|
||||
|
||||
func testDroppingPDFWhileDocumentOpenReplacesThroughAppState() async throws {
|
||||
let firstURL = try makeTemporaryPDF()
|
||||
let secondURL = try makeTemporaryPDF()
|
||||
defer {
|
||||
try? FileManager.default.removeItem(at: firstURL)
|
||||
try? FileManager.default.removeItem(at: secondURL)
|
||||
}
|
||||
|
||||
let appState = AppState()
|
||||
appState.loadDocument(from: firstURL)
|
||||
XCTAssertEqual(appState.documentURL, firstURL)
|
||||
|
||||
let provider = try XCTUnwrap(NSItemProvider(contentsOf: secondURL))
|
||||
XCTAssertTrue(appState.openDroppedDocument(from: [provider]))
|
||||
|
||||
try await waitUntil {
|
||||
appState.documentURL == secondURL
|
||||
}
|
||||
|
||||
XCTAssertEqual(appState.documentURL, secondURL)
|
||||
XCTAssertNotNil(appState.document)
|
||||
XCTAssertFalse(appState.hasUnsavedChanges)
|
||||
}
|
||||
|
||||
func testClosingDocumentReturnsToEmptyWindowWorkflow() throws {
|
||||
let url = try makeTemporaryPDF()
|
||||
defer { try? FileManager.default.removeItem(at: url) }
|
||||
|
||||
let appState = AppState()
|
||||
appState.loadDocument(from: url)
|
||||
appState.showLeftSidebar = true
|
||||
appState.leftSidebarMode = .annotations
|
||||
appState.showCommentsSidebar = true
|
||||
appState.sidebarMode = .highlights
|
||||
appState.searchText = "draft"
|
||||
appState.showToolbarSearch = true
|
||||
appState.collapsedPageIndexes = [0]
|
||||
|
||||
appState.closeDocument()
|
||||
|
||||
XCTAssertNil(appState.document)
|
||||
XCTAssertNil(appState.documentURL)
|
||||
XCTAssertFalse(appState.showLeftSidebar)
|
||||
XCTAssertEqual(appState.leftSidebarMode, .pages)
|
||||
XCTAssertFalse(appState.showCommentsSidebar)
|
||||
XCTAssertEqual(appState.sidebarMode, .annotations)
|
||||
XCTAssertEqual(appState.searchText, "")
|
||||
XCTAssertFalse(appState.showToolbarSearch)
|
||||
XCTAssertTrue(appState.annotations.isEmpty)
|
||||
XCTAssertTrue(appState.bookmarks.isEmpty)
|
||||
XCTAssertEqual(appState.currentPageIndex, 0)
|
||||
XCTAssertEqual(appState.pageText, "1")
|
||||
XCTAssertFalse(appState.hasUnsavedWork)
|
||||
XCTAssertEqual(appState.statusMessage, "Closed PDF.")
|
||||
}
|
||||
|
||||
func testCompactWorkflowShowsOnlyOneSidebarAtATime() {
|
||||
let appState = AppState()
|
||||
appState.updateWindowWidth(ReaderAdaptiveLayout.minimumWindowWidth)
|
||||
|
||||
appState.toggleRightSidebar(mode: .highlights)
|
||||
XCTAssertTrue(appState.showCommentsSidebar)
|
||||
XCTAssertEqual(appState.sidebarMode, .highlights)
|
||||
XCTAssertFalse(appState.showLeftSidebar)
|
||||
|
||||
appState.togglePageSidebar()
|
||||
XCTAssertTrue(appState.showLeftSidebar)
|
||||
XCTAssertEqual(appState.leftSidebarMode, .pages)
|
||||
XCTAssertFalse(appState.showCommentsSidebar)
|
||||
|
||||
appState.toggleRightSidebar(mode: .highlights)
|
||||
XCTAssertTrue(appState.showCommentsSidebar)
|
||||
XCTAssertEqual(appState.sidebarMode, .highlights)
|
||||
XCTAssertFalse(appState.showLeftSidebar)
|
||||
}
|
||||
|
||||
func testPageSidebarToggleClosesLeftSidebarEvenWhenMarksAreSelected() {
|
||||
let appState = AppState()
|
||||
appState.updateWindowWidth(1_280)
|
||||
appState.showLeftSidebar = true
|
||||
appState.leftSidebarMode = .annotations
|
||||
|
||||
appState.togglePageSidebar()
|
||||
|
||||
XCTAssertFalse(appState.showLeftSidebar)
|
||||
XCTAssertEqual(appState.leftSidebarMode, .annotations)
|
||||
}
|
||||
|
||||
func testRightSidebarToolbarToggleClosesAndReopensCurrentMode() {
|
||||
let appState = AppState()
|
||||
appState.updateWindowWidth(1_280)
|
||||
|
||||
appState.toggleRightSidebar(mode: .highlights)
|
||||
XCTAssertTrue(appState.showCommentsSidebar)
|
||||
XCTAssertEqual(appState.sidebarMode, .highlights)
|
||||
|
||||
appState.toggleRightSidebarVisibility()
|
||||
XCTAssertFalse(appState.showCommentsSidebar)
|
||||
XCTAssertEqual(appState.sidebarMode, .highlights)
|
||||
|
||||
appState.toggleRightSidebarVisibility()
|
||||
XCTAssertTrue(appState.showCommentsSidebar)
|
||||
XCTAssertEqual(appState.sidebarMode, .highlights)
|
||||
}
|
||||
|
||||
func testRightSidebarToggleClosesFromDifferentOpenMode() {
|
||||
let appState = AppState()
|
||||
appState.updateWindowWidth(1_280)
|
||||
|
||||
appState.toggleRightSidebar(mode: .highlights)
|
||||
XCTAssertTrue(appState.showCommentsSidebar)
|
||||
XCTAssertEqual(appState.sidebarMode, .highlights)
|
||||
|
||||
appState.toggleRightSidebar(mode: .annotations)
|
||||
XCTAssertFalse(appState.showCommentsSidebar)
|
||||
XCTAssertEqual(appState.sidebarMode, .highlights)
|
||||
|
||||
appState.toggleRightSidebar(mode: .annotations)
|
||||
XCTAssertTrue(appState.showCommentsSidebar)
|
||||
XCTAssertEqual(appState.sidebarMode, .annotations)
|
||||
}
|
||||
|
||||
func testRightSidebarToggleFromHighlightsDoesNotSwitchModeWhenClosing() {
|
||||
let appState = AppState()
|
||||
appState.updateWindowWidth(1_280)
|
||||
|
||||
appState.toggleRightSidebar(mode: .highlights)
|
||||
XCTAssertTrue(appState.showCommentsSidebar)
|
||||
XCTAssertEqual(appState.sidebarMode, .highlights)
|
||||
|
||||
appState.toggleRightSidebarVisibility()
|
||||
XCTAssertFalse(appState.showCommentsSidebar)
|
||||
XCTAssertEqual(appState.sidebarMode, .highlights)
|
||||
|
||||
appState.toggleRightSidebarVisibility()
|
||||
XCTAssertTrue(appState.showCommentsSidebar)
|
||||
XCTAssertEqual(appState.sidebarMode, .highlights)
|
||||
}
|
||||
|
||||
func testRightSidebarToolbarToggleDefaultsToCommentsWhenNoModeWasChosen() {
|
||||
let appState = AppState()
|
||||
appState.updateWindowWidth(1_280)
|
||||
|
||||
XCTAssertFalse(appState.showCommentsSidebar)
|
||||
XCTAssertEqual(appState.sidebarMode, .annotations)
|
||||
|
||||
appState.toggleRightSidebarVisibility()
|
||||
|
||||
XCTAssertTrue(appState.showCommentsSidebar)
|
||||
XCTAssertEqual(appState.sidebarMode, .annotations)
|
||||
}
|
||||
|
||||
func testRegularWorkflowAllowsNavigationAndReviewSidebarsTogether() {
|
||||
let appState = AppState()
|
||||
appState.updateWindowWidth(1_000)
|
||||
|
||||
appState.togglePageSidebar()
|
||||
appState.toggleRightSidebar(mode: .annotations)
|
||||
|
||||
XCTAssertTrue(appState.showLeftSidebar)
|
||||
XCTAssertEqual(appState.leftSidebarMode, .pages)
|
||||
XCTAssertTrue(appState.showCommentsSidebar)
|
||||
XCTAssertEqual(appState.sidebarMode, .annotations)
|
||||
}
|
||||
|
||||
func testSaveAvailabilityTracksReplyDraftAsUnsavedWork() throws {
|
||||
let url = try makeTemporaryPDF()
|
||||
defer { try? FileManager.default.removeItem(at: url) }
|
||||
|
||||
let appState = AppState()
|
||||
appState.loadDocument(from: url)
|
||||
|
||||
XCTAssertFalse(appState.hasUnsavedWork)
|
||||
XCTAssertFalse(appState.canSaveDocument)
|
||||
XCTAssertEqual(appState.saveHelpText, "No unsaved changes.")
|
||||
|
||||
appState.sidebarReplyDraft = "Need to verify this quote."
|
||||
|
||||
XCTAssertTrue(appState.hasUnsavedWork)
|
||||
XCTAssertTrue(appState.hasUnsentSidebarReplyDraft)
|
||||
XCTAssertTrue(appState.canSaveDocument)
|
||||
XCTAssertEqual(appState.saveHelpText, "Send or cancel the reply draft before saving.")
|
||||
|
||||
appState.hasUnsavedChanges = true
|
||||
XCTAssertTrue(appState.canSaveDocument)
|
||||
XCTAssertEqual(appState.saveHelpText, "Save PDF")
|
||||
}
|
||||
|
||||
private func makeTemporaryPDF() throws -> URL {
|
||||
let url = FileManager.default.temporaryDirectory
|
||||
.appendingPathComponent(UUID().uuidString)
|
||||
.appendingPathExtension("pdf")
|
||||
|
||||
let data = NSMutableData()
|
||||
guard let consumer = CGDataConsumer(data: data) else {
|
||||
throw TestPDFError.couldNotCreateConsumer
|
||||
}
|
||||
|
||||
var mediaBox = CGRect(x: 0, y: 0, width: 612, height: 792)
|
||||
guard let context = CGContext(consumer: consumer, mediaBox: &mediaBox, nil) else {
|
||||
throw TestPDFError.couldNotCreateContext
|
||||
}
|
||||
|
||||
context.beginPDFPage(nil)
|
||||
context.endPDFPage()
|
||||
context.closePDF()
|
||||
|
||||
try data.write(to: url, options: .atomic)
|
||||
return url
|
||||
}
|
||||
|
||||
private enum TestPDFError: Error {
|
||||
case couldNotCreateConsumer
|
||||
case couldNotCreateContext
|
||||
}
|
||||
|
||||
private func waitUntil(
|
||||
timeout: TimeInterval = 2,
|
||||
condition: @MainActor @escaping () -> Bool
|
||||
) async throws {
|
||||
let deadline = Date().addingTimeInterval(timeout)
|
||||
while Date() < deadline {
|
||||
if condition() {
|
||||
return
|
||||
}
|
||||
try await Task.sleep(nanoseconds: 20_000_000)
|
||||
}
|
||||
XCTFail("Timed out waiting for condition.")
|
||||
}
|
||||
}
|
||||
122
Tests/IHatePDFsTests/ReaderAdaptiveLayoutTests.swift
Normal file
122
Tests/IHatePDFsTests/ReaderAdaptiveLayoutTests.swift
Normal file
@@ -0,0 +1,122 @@
|
||||
import XCTest
|
||||
@testable import IHatePDFs
|
||||
|
||||
final class ReaderAdaptiveLayoutTests: XCTestCase {
|
||||
func testSizeClassBreakpointsMatchMacWindowProfiles() {
|
||||
XCTAssertEqual(ReaderAdaptiveLayout(width: 759).sizeClass, .compact)
|
||||
XCTAssertEqual(ReaderAdaptiveLayout(width: 959).sizeClass, .compact)
|
||||
XCTAssertEqual(ReaderAdaptiveLayout(width: 960).sizeClass, .regular)
|
||||
XCTAssertEqual(ReaderAdaptiveLayout(width: 1_279).sizeClass, .regular)
|
||||
XCTAssertEqual(ReaderAdaptiveLayout(width: 1_280).sizeClass, .wide)
|
||||
}
|
||||
|
||||
func testCompactProfileKeepsSingleSidebarAndDocumentReadableAtMinimumWidth() {
|
||||
let layout = ReaderAdaptiveLayout(width: ReaderAdaptiveLayout.minimumWindowWidth)
|
||||
XCTAssertEqual(layout.sizeClass, .compact)
|
||||
XCTAssertFalse(layout.allowsDualSidebars)
|
||||
XCTAssertTrue(layout.usesCompactToolbar)
|
||||
|
||||
assertDocumentWidthIsPreserved(
|
||||
layout: layout,
|
||||
availableWidth: ReaderAdaptiveLayout.minimumWindowWidth,
|
||||
requestedLeft: layout.leftSidebarMaxWidth,
|
||||
requestedRight: 0,
|
||||
showLeft: true,
|
||||
showRight: false
|
||||
)
|
||||
|
||||
assertDocumentWidthIsPreserved(
|
||||
layout: layout,
|
||||
availableWidth: ReaderAdaptiveLayout.minimumWindowWidth,
|
||||
requestedLeft: 0,
|
||||
requestedRight: layout.rightSidebarMaxWidth,
|
||||
showLeft: false,
|
||||
showRight: true
|
||||
)
|
||||
}
|
||||
|
||||
func testRegularProfilePreservesDocumentWidthWithBothSidebarsAtBreakpoint() {
|
||||
let layout = ReaderAdaptiveLayout(width: 960)
|
||||
XCTAssertEqual(layout.sizeClass, .regular)
|
||||
XCTAssertTrue(layout.allowsDualSidebars)
|
||||
XCTAssertFalse(layout.usesCompactToolbar)
|
||||
|
||||
assertDocumentWidthIsPreserved(
|
||||
layout: layout,
|
||||
availableWidth: 960,
|
||||
requestedLeft: layout.leftSidebarIdealWidth,
|
||||
requestedRight: layout.rightSidebarIdealWidth,
|
||||
showLeft: true,
|
||||
showRight: true
|
||||
)
|
||||
}
|
||||
|
||||
func testExpandedRegularSidebarsShrinkBeforeTheDocumentDoes() {
|
||||
let layout = ReaderAdaptiveLayout(width: 960)
|
||||
|
||||
let widths = layout.resolvedSidebarWidths(
|
||||
availableWidth: 960,
|
||||
requestedLeft: layout.leftSidebarMaxWidth,
|
||||
requestedRight: layout.rightSidebarMaxWidth,
|
||||
showLeft: true,
|
||||
showRight: true
|
||||
)
|
||||
|
||||
XCTAssertGreaterThanOrEqual(widths.left, layout.leftSidebarMinWidth - 0.001)
|
||||
XCTAssertGreaterThanOrEqual(widths.right, layout.rightSidebarMinWidth - 0.001)
|
||||
XCTAssertLessThanOrEqual(widths.left, layout.leftSidebarMaxWidth + 0.001)
|
||||
XCTAssertLessThanOrEqual(widths.right, layout.rightSidebarMaxWidth + 0.001)
|
||||
assertDocumentWidthIsPreserved(
|
||||
layout: layout,
|
||||
availableWidth: 960,
|
||||
requestedLeft: layout.leftSidebarMaxWidth,
|
||||
requestedRight: layout.rightSidebarMaxWidth,
|
||||
showLeft: true,
|
||||
showRight: true
|
||||
)
|
||||
}
|
||||
|
||||
func testWideProfileUsesRoomierSidebarsWhilePreservingDocumentWidth() {
|
||||
let layout = ReaderAdaptiveLayout(width: 1_280)
|
||||
XCTAssertEqual(layout.sizeClass, .wide)
|
||||
XCTAssertGreaterThan(layout.rightSidebarIdealWidth, ReaderAdaptiveLayout(width: 960).rightSidebarIdealWidth)
|
||||
XCTAssertGreaterThan(layout.documentMinWidth, ReaderAdaptiveLayout(width: 960).documentMinWidth)
|
||||
|
||||
assertDocumentWidthIsPreserved(
|
||||
layout: layout,
|
||||
availableWidth: 1_280,
|
||||
requestedLeft: layout.leftSidebarIdealWidth,
|
||||
requestedRight: layout.rightSidebarIdealWidth,
|
||||
showLeft: true,
|
||||
showRight: true
|
||||
)
|
||||
}
|
||||
|
||||
private func assertDocumentWidthIsPreserved(
|
||||
layout: ReaderAdaptiveLayout,
|
||||
availableWidth: CGFloat,
|
||||
requestedLeft: CGFloat,
|
||||
requestedRight: CGFloat,
|
||||
showLeft: Bool,
|
||||
showRight: Bool,
|
||||
file: StaticString = #filePath,
|
||||
line: UInt = #line
|
||||
) {
|
||||
let widths = layout.resolvedSidebarWidths(
|
||||
availableWidth: availableWidth,
|
||||
requestedLeft: requestedLeft,
|
||||
requestedRight: requestedRight,
|
||||
showLeft: showLeft,
|
||||
showRight: showRight
|
||||
)
|
||||
let documentWidth = layout.visibleContentWidth(
|
||||
availableWidth: availableWidth,
|
||||
leftWidth: widths.left,
|
||||
rightWidth: widths.right,
|
||||
showLeft: showLeft,
|
||||
showRight: showRight
|
||||
)
|
||||
|
||||
XCTAssertGreaterThanOrEqual(documentWidth, layout.documentMinWidth - 0.001, file: file, line: line)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user