Previously the chapter reader showed vocab tables as a flat list of OCR lines — because Vision reads columns top-to-bottom, the Spanish column appeared as one block followed by the English column, making pairings illegible. Now every vocab table renders as a 2-column grid with Spanish on the left and English on the right. Supporting changes: - New ocr_all_vocab.swift: bounding-box OCR over all 931 vocab images, cluster lines into rows by Y-coordinate, split rows by largest X-gap, detect 2- / 3- / 4-column layouts automatically. ~2800 pairs extracted this pass vs ~1100 from the old block-alternation heuristic. - merge_pdf_into_book.py now prefers bounding-box pairs when present, falls back to the heuristic, embeds the resulting pairs as vocab_table.cards in book.json. - DataLoader passes cards through to TextbookBlock on seed. - TextbookChapterView renders cards via SwiftUI Grid (2 cols). - fix_vocab.py quarantine rule relaxed — only mis-pairs where both sides are clearly the same language are removed. "unknown" sides stay (bbox pipeline already oriented them correctly). Textbook card count jumps from 1044 → 3118 active pairs. textbookDataVersion bumped to 9. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
54 lines
1.7 KiB
Swift
54 lines
1.7 KiB
Swift
import XCTest
|
|
|
|
final class VocabGridTests: XCTestCase {
|
|
|
|
override func setUpWithError() throws {
|
|
continueAfterFailure = false
|
|
}
|
|
|
|
/// Verifies the chapter reader renders vocab tables as a paired Spanish↔English grid.
|
|
func testChapter4VocabGrid() throws {
|
|
let app = XCUIApplication()
|
|
app.launchArguments += ["-onboardingComplete", "YES"]
|
|
app.launch()
|
|
|
|
app.tabBars.buttons["Course"].tap()
|
|
|
|
let textbookRow = app.buttons.containing(NSPredicate(
|
|
format: "label CONTAINS[c] 'Complete Spanish'"
|
|
)).firstMatch
|
|
XCTAssertTrue(textbookRow.waitForExistence(timeout: 5))
|
|
textbookRow.tap()
|
|
|
|
let ch4 = app.buttons["textbook-chapter-row-4"]
|
|
XCTAssertTrue(ch4.waitForExistence(timeout: 3))
|
|
ch4.tap()
|
|
|
|
attach(app, name: "01-ch4-top")
|
|
|
|
// Tap the first vocab disclosure — "Vocabulary (N items)"
|
|
let vocabButton = app.buttons.matching(NSPredicate(
|
|
format: "label BEGINSWITH 'Vocabulary ('"
|
|
)).firstMatch
|
|
XCTAssertTrue(vocabButton.waitForExistence(timeout: 3))
|
|
vocabButton.tap()
|
|
Thread.sleep(forTimeInterval: 0.4)
|
|
|
|
attach(app, name: "02-ch4-vocab-open")
|
|
|
|
// Scroll a little and screenshot a deeper vocab — numbers table is
|
|
// typically a few screens down in chapter 4.
|
|
app.swipeUp(velocity: .fast)
|
|
app.swipeUp(velocity: .fast)
|
|
attach(app, name: "03-ch4-deeper")
|
|
}
|
|
|
|
private func attach(_ app: XCUIApplication, name: String) {
|
|
let s = app.screenshot()
|
|
let a = XCTAttachment(screenshot: s)
|
|
a.name = name
|
|
a.lifetime = .keepAlways
|
|
add(a)
|
|
}
|
|
}
|