ContactHeuristicParserSpatialTest.swift
143 lignes · 5676 octets
import XCTest @testable import Card2vcf final class ContactHeuristicParserSpatialTest: XCTestCase { private func ligne( _ text: String, top: Int, height: Int = 30, left: Int = 40, right: Int = 500, confidence: Float = 90 ) -> OcrLine { OcrLine(text: text, box: OcrBox(left: left, top: top, right: right, bottom: top + height), confidence: confidence) } private func ocr( _ lines: [OcrLine], phones: [String] = [], emails: [String] = [], urls: [String] = [] ) -> OcrResult { OcrResult( rawText: lines.map(\.text).joined(separator: "\n"), lines: lines.map(\.text), phones: phones, emails: emails, urls: urls, spatialLines: lines ) } func testAdresseMultiLignesAssemblee() { let card = ContactHeuristicParser.parse(ocr([ ligne("Sonia MARTIN", top: 100, height: 40), ligne("561 allée des Noisetiers", top: 400), ligne("69760 Limonest - France", top: 440), ])) XCTAssertEqual("561 allée des Noisetiers, 69760 Limonest - France", card.address) } func testNomEnGrosCaracteresSansMajusculesDetecte() { let card = ContactHeuristicParser.parse(ocr( [ ligne("sonia martin", top: 100, height: 64), ligne("directrice commerciale", top: 180, height: 28), ], emails: ["sonia.martin@voltea.fr"] )) XCTAssertEqual("Martin", card.lastName) XCTAssertEqual("sonia", card.firstName?.lowercased()) XCTAssertEqual("directrice commerciale", card.jobTitle) } func testSocieteDominanteEnMajusculesSepareeDuNom() { let card = ContactHeuristicParser.parse(ocr( [ ligne("VOLTEA SOLUTIONS", top: 40, height: 50), ligne("Sonia MARTIN", top: 200, height: 34), ligne("Responsable grands comptes", top: 244, height: 28), ], emails: ["sonia.martin@voltea.fr"] )) XCTAssertEqual("VOLTEA SOLUTIONS", card.company) XCTAssertEqual("Sonia Martin", card.fullName) XCTAssertEqual("Responsable grands comptes", card.jobTitle) } func testNomToutEnMajusculesResteUnNomPasUneSociete() { // Très courant en France : « SONIA MARTIN » tout en capitales. let card = ContactHeuristicParser.parse(ocr( [ ligne("SONIA MARTIN", top: 100, height: 34), ligne("Directrice commerciale", top: 150, height: 28), ], emails: ["sonia.martin@voltea.fr"] )) XCTAssertEqual("Sonia Martin", card.fullName) XCTAssertEqual("Martin", card.lastName) XCTAssertEqual("Voltea", card.company) // depuis le domaine email, pas le nom } func testDeuxColonnesSansMelangeAdresseTelephone() { let card = ContactHeuristicParser.parse(ocr( [ ligne("Sonia MARTIN", top: 60, height: 40, left: 40, right: 900), ligne("12 rue des Fleurs", top: 400, left: 40, right: 380), ligne("75011 Paris", top: 440, left: 40, right: 380), ligne("Tél. 04 75 35 12 34", top: 405, left: 600, right: 980), ligne("sonia@voltea.fr", top: 445, left: 600, right: 980), ], phones: ["0475351234"], emails: ["sonia@voltea.fr"] )) XCTAssertEqual("12 rue des Fleurs, 75011 Paris", card.address) XCTAssertEqual(["0475351234"], card.phones) } func testLibelleTelephoneAvecValeurSurLaLigneSuivante() { let card = ContactHeuristicParser.parse(ocr([ ligne("Sonia MARTIN", top: 60, height: 40), ligne("Tél. :", top: 300), ligne("04 75 35 12 34", top: 340), ])) XCTAssertTrue(card.phones.contains("0475351234"), "téléphone attendu: \(card.phones)") } func testChampsDouteuxMarquesSousLeSeuilDeConfiance() { let card = ContactHeuristicParser.parse(ocr([ ligne("Sonia MARTIN", top: 100, height: 40, confidence: 45), ligne("561 allée des Noisetiers", top: 400, confidence: 92), ligne("69760 Limonest - France", top: 440, confidence: 91), ])) XCTAssertTrue(card.champsDouteux.contains("fullName"), "nom à 45% doit être douteux: \(card.champsDouteux)") XCTAssertFalse(card.champsDouteux.contains("address"), "adresse à ~91% doit être sûre: \(card.champsDouteux)") } func testFallbackSansBoxesAucunChampDouteux() { let card = ContactHeuristicParser.parse( OcrResult(rawText: "Sonia MARTIN\nDirectrice", lines: ["Sonia MARTIN", "Directrice"]) ) XCTAssertEqual([], card.champsDouteux) } func testCheminSpatialSeulementAvecBoxes() { let textes = [ "Sonia MARTIN", "561 allée des Noisetiers", "69760 Limonest - France", ] let avecBoxes = ContactHeuristicParser.parse(ocr([ ligne(textes[0], top: 100, height: 40), ligne(textes[1], top: 400), ligne(textes[2], top: 440), ])) let sansBoxes = ContactHeuristicParser.parse( OcrResult(rawText: textes.joined(separator: "\n"), lines: textes) ) // Spatial : adresse complète ; legacy : une seule ligne (la plus longue). XCTAssertEqual("561 allée des Noisetiers, 69760 Limonest - France", avecBoxes.address) XCTAssertEqual("561 allée des Noisetiers", sansBoxes.address) } }
GitRust