@@ -624,4 +624,344 @@ public function testSplitIntoSentencesWithRtlLanguage(): void
624624 // Clean up
625625 Connection::query ("DELETE FROM $ languages WHERE LgID = $ rtlLangId " );
626626 }
627+
628+ // ===== checkText() tests =====
629+
630+ public function testCheckTextReturnsArrayWithStats (): void
631+ {
632+ if (!self ::$ dbConnected ) {
633+ $ this ->markTestSkipped ('Database connection required ' );
634+ }
635+
636+ $ text = "Hello world. This is a test sentence. " ;
637+ $ result = TextParsing::checkText ($ text , self ::$ testLanguageId );
638+
639+ $ this ->assertIsArray ($ result );
640+ $ this ->assertArrayHasKey ('sentences ' , $ result );
641+ $ this ->assertArrayHasKey ('words ' , $ result );
642+ $ this ->assertArrayHasKey ('unknownPercent ' , $ result );
643+ $ this ->assertArrayHasKey ('preview ' , $ result );
644+ }
645+
646+ public function testCheckTextReturnsSentenceCount (): void
647+ {
648+ if (!self ::$ dbConnected ) {
649+ $ this ->markTestSkipped ('Database connection required ' );
650+ }
651+
652+ $ text = "First sentence. Second sentence. Third sentence. " ;
653+ $ result = TextParsing::checkText ($ text , self ::$ testLanguageId );
654+
655+ // At least 1 sentence should be parsed
656+ $ this ->assertGreaterThanOrEqual (1 , $ result ['sentences ' ]);
657+ }
658+
659+ public function testCheckTextReturnsWordCount (): void
660+ {
661+ if (!self ::$ dbConnected ) {
662+ $ this ->markTestSkipped ('Database connection required ' );
663+ }
664+
665+ $ text = "One two three four five. " ;
666+ $ result = TextParsing::checkText ($ text , self ::$ testLanguageId );
667+
668+ $ this ->assertGreaterThanOrEqual (5 , $ result ['words ' ]);
669+ }
670+
671+ public function testCheckTextReturnsUnknownPercent (): void
672+ {
673+ if (!self ::$ dbConnected ) {
674+ $ this ->markTestSkipped ('Database connection required ' );
675+ }
676+
677+ $ text = "Hello world. " ;
678+ $ result = TextParsing::checkText ($ text , self ::$ testLanguageId );
679+
680+ // Without any words in the database, all words should be unknown
681+ $ this ->assertIsFloat ($ result ['unknownPercent ' ]);
682+ $ this ->assertGreaterThanOrEqual (0 , $ result ['unknownPercent ' ]);
683+ $ this ->assertLessThanOrEqual (100 , $ result ['unknownPercent ' ]);
684+ }
685+
686+ public function testCheckTextReturnsPreview (): void
687+ {
688+ if (!self ::$ dbConnected ) {
689+ $ this ->markTestSkipped ('Database connection required ' );
690+ }
691+
692+ $ text = "First sentence. Second sentence. Third sentence. " ;
693+ $ result = TextParsing::checkText ($ text , self ::$ testLanguageId );
694+
695+ $ this ->assertIsString ($ result ['preview ' ]);
696+ $ this ->assertNotEmpty ($ result ['preview ' ]);
697+ }
698+
699+ public function testCheckTextPreviewWithMultipleSentences (): void
700+ {
701+ if (!self ::$ dbConnected ) {
702+ $ this ->markTestSkipped ('Database connection required ' );
703+ }
704+
705+ $ text = "Sentence one. Sentence two. Sentence three. Sentence four. Sentence five. " ;
706+ $ result = TextParsing::checkText ($ text , self ::$ testLanguageId );
707+
708+ // Preview should contain some of the text
709+ $ this ->assertIsString ($ result ['preview ' ]);
710+ $ this ->assertNotEmpty ($ result ['preview ' ]);
711+ }
712+
713+ public function testCheckTextWithEmptyText (): void
714+ {
715+ if (!self ::$ dbConnected ) {
716+ $ this ->markTestSkipped ('Database connection required ' );
717+ }
718+
719+ $ result = TextParsing::checkText ('' , self ::$ testLanguageId );
720+
721+ $ this ->assertIsArray ($ result );
722+ $ this ->assertEquals (0 , $ result ['words ' ]);
723+ }
724+
725+ public function testCheckTextWithInvalidLanguage (): void
726+ {
727+ if (!self ::$ dbConnected ) {
728+ $ this ->markTestSkipped ('Database connection required ' );
729+ }
730+
731+ $ result = TextParsing::checkText ('Test text. ' , 99999 );
732+
733+ $ this ->assertIsArray ($ result );
734+ $ this ->assertEquals (0 , $ result ['sentences ' ]);
735+ $ this ->assertEquals (0 , $ result ['words ' ]);
736+ $ this ->assertEquals (100.0 , $ result ['unknownPercent ' ]);
737+ $ this ->assertEquals ('' , $ result ['preview ' ]);
738+ }
739+
740+ // ===== parseAndSave() error handling tests =====
741+
742+ public function testParseAndSaveThrowsForZeroTextId (): void
743+ {
744+ if (!self ::$ dbConnected ) {
745+ $ this ->markTestSkipped ('Database connection required ' );
746+ }
747+
748+ $ this ->expectException (\InvalidArgumentException::class);
749+ $ this ->expectExceptionMessage ('Text ID must be positive ' );
750+
751+ TextParsing::parseAndSave ("Test text. " , self ::$ testLanguageId , 0 );
752+ }
753+
754+ public function testParseAndSaveThrowsForNegativeTextId (): void
755+ {
756+ if (!self ::$ dbConnected ) {
757+ $ this ->markTestSkipped ('Database connection required ' );
758+ }
759+
760+ $ this ->expectException (\InvalidArgumentException::class);
761+ $ this ->expectExceptionMessage ('Text ID must be positive ' );
762+
763+ TextParsing::parseAndSave ("Test text. " , self ::$ testLanguageId , -1 );
764+ }
765+
766+ public function testParseAndSaveThrowsForInvalidLanguage (): void
767+ {
768+ if (!self ::$ dbConnected ) {
769+ $ this ->markTestSkipped ('Database connection required ' );
770+ }
771+
772+ $ this ->expectException (\Lwt \Core \Exception \DatabaseException::class);
773+
774+ TextParsing::parseAndSave ("Test text. " , 99999 , 1 );
775+ }
776+
777+ // ===== parseAndDisplayPreview() error handling tests =====
778+
779+ public function testParseAndDisplayPreviewThrowsForInvalidLanguage (): void
780+ {
781+ if (!self ::$ dbConnected ) {
782+ $ this ->markTestSkipped ('Database connection required ' );
783+ }
784+
785+ $ this ->expectException (\Lwt \Core \Exception \DatabaseException::class);
786+
787+ ob_start ();
788+ try {
789+ TextParsing::parseAndDisplayPreview ("Test text. " , 99999 );
790+ } finally {
791+ ob_end_clean ();
792+ }
793+ }
794+
795+ // ===== Multi-word expression tests =====
796+
797+ public function testParseAndSaveWithMultiWordExpression (): void
798+ {
799+ if (!self ::$ dbConnected ) {
800+ $ this ->markTestSkipped ('Database connection required ' );
801+ }
802+
803+ $ texts = Globals::table ('texts ' );
804+ $ sentences = Globals::table ('sentences ' );
805+ $ word_occurrences = Globals::table ('word_occurrences ' );
806+ $ words = Globals::table ('words ' );
807+
808+ // Create a multi-word expression (lowercase to match parsed text)
809+ $ sql = "INSERT INTO $ words (WoLgID, WoText, WoTextLC, WoTranslation, WoStatus, WoWordCount)
810+ VALUES ( " . self ::$ testLanguageId . ", 'test word', 'test word', 'translation', 1, 2) " ;
811+ Connection::query ($ sql );
812+ $ wordId = mysqli_insert_id (Globals::getDbConnection ());
813+
814+ // Create a test text
815+ $ sql = "INSERT INTO $ texts (TxLgID, TxTitle, TxText, TxAudioURI)
816+ VALUES ( " . self ::$ testLanguageId . ", 'MW Test', 'This is a test word example.', '') " ;
817+ Connection::query ($ sql );
818+ $ textId = mysqli_insert_id (Globals::getDbConnection ());
819+
820+ // Parse and save
821+ TextParsing::parseAndSave ("This is a test word example. " , self ::$ testLanguageId , $ textId );
822+
823+ // Check that word occurrences were created
824+ $ itemCount = Connection::fetchValue (
825+ "SELECT COUNT(*) as value FROM $ word_occurrences WHERE Ti2TxID = $ textId "
826+ );
827+ $ this ->assertGreaterThan (0 , (int )$ itemCount , 'Should have word occurrences ' );
828+
829+ // Clean up
830+ Connection::query ("DELETE FROM $ word_occurrences WHERE Ti2TxID = $ textId " );
831+ Connection::query ("DELETE FROM $ sentences WHERE SeTxID = $ textId " );
832+ Connection::query ("DELETE FROM $ texts WHERE TxID = $ textId " );
833+ Connection::query ("DELETE FROM $ words WHERE WoID = $ wordId " );
834+ }
835+
836+ // ===== Additional edge case tests =====
837+
838+ public function testSplitIntoSentencesWithAbbreviations (): void
839+ {
840+ if (!self ::$ dbConnected ) {
841+ $ this ->markTestSkipped ('Database connection required ' );
842+ }
843+
844+ // Test that abbreviations don't split sentences incorrectly
845+ $ text = "Dr. Smith works at Mr. Jones Corp. He is great. " ;
846+ $ result = $ this ->callSplitIntoSentences ($ text , self ::$ testLanguageId );
847+
848+ $ this ->assertIsArray ($ result );
849+ // Should not split at "Dr." or "Mr."
850+ $ this ->assertGreaterThanOrEqual (1 , count ($ result ));
851+ }
852+
853+ public function testSplitIntoSentencesWithMixedPunctuation (): void
854+ {
855+ if (!self ::$ dbConnected ) {
856+ $ this ->markTestSkipped ('Database connection required ' );
857+ }
858+
859+ $ text = "What? How! Sure... Go on. Yes! " ;
860+ $ result = $ this ->callSplitIntoSentences ($ text , self ::$ testLanguageId );
861+
862+ $ this ->assertIsArray ($ result );
863+ $ this ->assertGreaterThanOrEqual (4 , count ($ result ));
864+ }
865+
866+ public function testSplitIntoSentencesPreservesParagraphMarkers (): void
867+ {
868+ if (!self ::$ dbConnected ) {
869+ $ this ->markTestSkipped ('Database connection required ' );
870+ }
871+
872+ $ text = "Para one. \n\nPara two. \n\nPara three. " ;
873+ $ result = $ this ->callSplitIntoSentences ($ text , self ::$ testLanguageId );
874+
875+ $ this ->assertIsArray ($ result );
876+ // Should have paragraph markers (¶)
877+ $ joined = implode ('' , $ result );
878+ $ this ->assertStringContainsString ('¶ ' , $ joined );
879+ }
880+
881+ public function testCheckTextWithKnownWord (): void
882+ {
883+ if (!self ::$ dbConnected ) {
884+ $ this ->markTestSkipped ('Database connection required ' );
885+ }
886+
887+ $ words = Globals::table ('words ' );
888+
889+ // Create a known word
890+ $ sql = "INSERT INTO $ words (WoLgID, WoText, WoTextLC, WoTranslation, WoStatus, WoWordCount)
891+ VALUES ( " . self ::$ testLanguageId . ", 'known', 'known', 'bekannt', 99, 1) " ;
892+ Connection::query ($ sql );
893+ $ wordId = mysqli_insert_id (Globals::getDbConnection ());
894+
895+ $ result = TextParsing::checkText ('known word test. ' , self ::$ testLanguageId );
896+
897+ // At least one word should be known now (lower unknown %)
898+ $ this ->assertIsFloat ($ result ['unknownPercent ' ]);
899+ // Can't assert exact percentage since "word" and "test" are still unknown
900+
901+ // Clean up
902+ Connection::query ("DELETE FROM $ words WHERE WoID = $ wordId " );
903+ }
904+
905+ public function testParseAndSaveMultipleSentences (): void
906+ {
907+ if (!self ::$ dbConnected ) {
908+ $ this ->markTestSkipped ('Database connection required ' );
909+ }
910+
911+ $ texts = Globals::table ('texts ' );
912+ $ sentences = Globals::table ('sentences ' );
913+ $ word_occurrences = Globals::table ('word_occurrences ' );
914+
915+ // Create a test text with multiple sentences
916+ $ sql = "INSERT INTO $ texts (TxLgID, TxTitle, TxText, TxAudioURI)
917+ VALUES ( " . self ::$ testLanguageId . ", 'Multi Sentence Test', 'Sentence one. Sentence two. Sentence three.', '') " ;
918+ Connection::query ($ sql );
919+ $ textId = mysqli_insert_id (Globals::getDbConnection ());
920+
921+ TextParsing::parseAndSave ("Sentence one. Sentence two. Sentence three. " , self ::$ testLanguageId , $ textId );
922+
923+ // Check that at least 1 sentence was created
924+ $ sentenceCount = Connection::fetchValue (
925+ "SELECT COUNT(*) as value FROM $ sentences WHERE SeTxID = $ textId "
926+ );
927+ $ this ->assertGreaterThanOrEqual (1 , (int )$ sentenceCount , 'Should create at least 1 sentence ' );
928+
929+ // Clean up
930+ Connection::query ("DELETE FROM $ word_occurrences WHERE Ti2TxID = $ textId " );
931+ Connection::query ("DELETE FROM $ sentences WHERE SeTxID = $ textId " );
932+ Connection::query ("DELETE FROM $ texts WHERE TxID = $ textId " );
933+ }
934+
935+ public function testParseAndDisplayPreviewOutputsHtml (): void
936+ {
937+ if (!self ::$ dbConnected ) {
938+ $ this ->markTestSkipped ('Database connection required ' );
939+ }
940+
941+ ob_start ();
942+ TextParsing::parseAndDisplayPreview ("Test sentence one. Test sentence two. " , self ::$ testLanguageId );
943+ $ output = ob_get_clean ();
944+
945+ // Should output HTML structure
946+ $ this ->assertStringContainsString ('<h4> ' , $ output );
947+ $ this ->assertStringContainsString ('Sentences ' , $ output );
948+ $ this ->assertStringContainsString ('<ol> ' , $ output );
949+ $ this ->assertStringContainsString ('<li> ' , $ output );
950+ }
951+
952+ public function testParseAndDisplayPreviewOutputsJson (): void
953+ {
954+ if (!self ::$ dbConnected ) {
955+ $ this ->markTestSkipped ('Database connection required ' );
956+ }
957+
958+ ob_start ();
959+ TextParsing::parseAndDisplayPreview ("Hello world. " , self ::$ testLanguageId );
960+ $ output = ob_get_clean ();
961+
962+ // Should output JSON config scripts
963+ $ this ->assertStringContainsString ('text-check-words-config ' , $ output );
964+ $ this ->assertStringContainsString ('text-check-config ' , $ output );
965+ $ this ->assertStringContainsString ('application/json ' , $ output );
966+ }
627967}
0 commit comments