1e9e85398SGreg Roach<?php 23976b470SGreg Roach 3e9e85398SGreg Roach/** 4e9e85398SGreg Roach * webtrees: online genealogy 5d11be702SGreg Roach * Copyright (C) 2023 webtrees development team 6e9e85398SGreg Roach * This program is free software: you can redistribute it and/or modify 7e9e85398SGreg Roach * it under the terms of the GNU General Public License as published by 8e9e85398SGreg Roach * the Free Software Foundation, either version 3 of the License, or 9e9e85398SGreg Roach * (at your option) any later version. 10e9e85398SGreg Roach * This program is distributed in the hope that it will be useful, 11e9e85398SGreg Roach * but WITHOUT ANY WARRANTY; without even the implied warranty of 12e9e85398SGreg Roach * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13e9e85398SGreg Roach * GNU General Public License for more details. 14e9e85398SGreg Roach * You should have received a copy of the GNU General Public License 1589f7189bSGreg Roach * along with this program. If not, see <https://www.gnu.org/licenses/>. 16e9e85398SGreg Roach */ 17fcfa147eSGreg Roach 18e9e85398SGreg Roachdeclare(strict_types=1); 19e9e85398SGreg Roach 20e9e85398SGreg Roachnamespace Fisharebest\Webtrees; 21e9e85398SGreg Roach 2271378461SGreg Roachuse Fig\Http\Message\RequestMethodInterface; 2371378461SGreg Roachuse Fig\Http\Message\StatusCodeInterface; 247dcc0dd1SGreg Roachuse Fisharebest\Webtrees\Contracts\UserInterface; 2587cca37cSGreg Roachuse Fisharebest\Webtrees\Module\IndividualListModule; 267dcc0dd1SGreg Roachuse Fisharebest\Webtrees\Services\GedcomImportService; 277dcc0dd1SGreg Roachuse Fisharebest\Webtrees\Services\TreeService; 287dcc0dd1SGreg Roachuse Fisharebest\Webtrees\Services\UserService; 29*202c018bSGreg Roachuse PHPUnit\Framework\Attributes\CoversClass; 30*202c018bSGreg Roach 317dcc0dd1SGreg Roachuse function array_map; 327dcc0dd1SGreg Roachuse function preg_match_all; 33b9fc687eSGreg Roach 34*202c018bSGreg Roach#[CoversClass(IndividualListModule::class)] 35e5a6b4d4SGreg Roachclass IndividualListTest extends TestCase 36e9e85398SGreg Roach{ 37cd94ca66SGreg Roach protected static bool $uses_database = true; 38e9e85398SGreg Roach 397dcc0dd1SGreg Roach private Tree $tree; 407dcc0dd1SGreg Roach private User $user; 417dcc0dd1SGreg Roach 427dcc0dd1SGreg Roach public function setUp(): void 437dcc0dd1SGreg Roach { 44a26ec5edSGreg Roach parent::setUp(); 45a26ec5edSGreg Roach 467dcc0dd1SGreg Roach I18N::init('en-US'); 477dcc0dd1SGreg Roach 487dcc0dd1SGreg Roach $user_service = new UserService(); 497dcc0dd1SGreg Roach $tree_service = new TreeService(new GedcomImportService()); 507dcc0dd1SGreg Roach $this->tree = $tree_service->create('name', 'title'); 517dcc0dd1SGreg Roach $this->user = $user_service->create('user', 'User', 'user@example.com', 'secret'); 527dcc0dd1SGreg Roach $this->user->setPreference(UserInterface::PREF_IS_ADMINISTRATOR, '1'); 537dcc0dd1SGreg Roach $this->user->setPreference(UserInterface::PREF_AUTO_ACCEPT_EDITS, '1'); 547dcc0dd1SGreg Roach Auth::login($this->user); 557dcc0dd1SGreg Roach // The default "John Doe" individual will confuse the test results... 567dcc0dd1SGreg Roach Registry::individualFactory()->make('X1', $this->tree)->deleteRecord(); 577dcc0dd1SGreg Roach } 587dcc0dd1SGreg Roach 597dcc0dd1SGreg Roach public function testCollationOfInitials(): void 60e9e85398SGreg Roach { 61a26ec5edSGreg Roach $module = new IndividualListModule(); 62a26ec5edSGreg Roach 637dcc0dd1SGreg Roach $this->tree->createIndividual("0 @@ INDI\n1 NAME /Âaa/"); 647dcc0dd1SGreg Roach $this->tree->createIndividual("0 @@ INDI\n1 NAME /aaa/"); 657dcc0dd1SGreg Roach $this->tree->createIndividual("0 @@ INDI\n1 NAME /Ååå/"); 667dcc0dd1SGreg Roach $this->tree->createIndividual("0 @@ INDI\n1 NAME /æææ/"); 677dcc0dd1SGreg Roach $this->tree->createIndividual("0 @@ INDI\n1 NAME /Caa/"); 687dcc0dd1SGreg Roach $this->tree->createIndividual("0 @@ INDI\n1 NAME /Css/"); 697dcc0dd1SGreg Roach $this->tree->createIndividual("0 @@ INDI\n1 NAME /Dza/"); 70b9fc687eSGreg Roach 717dcc0dd1SGreg Roach I18N::init('en-US'); 727dcc0dd1SGreg Roach $request = self::createRequest(RequestMethodInterface::METHOD_GET, [], [], [], ['tree' => $this->tree]); 73a26ec5edSGreg Roach $response = $module->handle($request); 747dcc0dd1SGreg Roach $html = $response->getBody()->getContents(); 757dcc0dd1SGreg Roach preg_match_all('/%2Findividual-list&alpha=([^"&]+)/', $html, $matches); 767dcc0dd1SGreg Roach self::assertEquals(['A', 'C', 'D', 'Æ'], array_map(rawurldecode(...), $matches[1])); 77b9fc687eSGreg Roach 787dcc0dd1SGreg Roach I18N::init('sv'); 797dcc0dd1SGreg Roach $request = self::createRequest(RequestMethodInterface::METHOD_GET, [], [], [], ['tree' => $this->tree]); 80a26ec5edSGreg Roach $response = $module->handle($request); 817dcc0dd1SGreg Roach $html = $response->getBody()->getContents(); 827dcc0dd1SGreg Roach preg_match_all('/%2Findividual-list&alpha=([^"&]+)/', $html, $matches); 837dcc0dd1SGreg Roach self::assertEquals(['A', 'C', 'D', 'Å', 'Æ'], array_map(rawurldecode(...), $matches[1])); 84b9fc687eSGreg Roach 857dcc0dd1SGreg Roach I18N::init('hu'); 867dcc0dd1SGreg Roach $request = self::createRequest(RequestMethodInterface::METHOD_GET, [], [], [], ['tree' => $this->tree]); 87a26ec5edSGreg Roach $response = $module->handle($request); 887dcc0dd1SGreg Roach $html = $response->getBody()->getContents(); 897dcc0dd1SGreg Roach preg_match_all('/%2Findividual-list&alpha=([^"&]+)/', $html, $matches); 907dcc0dd1SGreg Roach self::assertEquals(['A', 'C', 'CS', 'DZ', 'Æ'], array_map(rawurldecode(...), $matches[1])); 917dcc0dd1SGreg Roach } 92a67cd39fSGreg Roach 93a26ec5edSGreg Roach public function xtestRedirectToCanonicalSurname(): void 947dcc0dd1SGreg Roach { 95a26ec5edSGreg Roach $module = new IndividualListModule(); 96a26ec5edSGreg Roach 977dcc0dd1SGreg Roach I18N::init('en-US'); 98a26ec5edSGreg Roach $request = self::createRequest(RequestMethodInterface::METHOD_GET, ['surname' => 'Muller'], [], [], ['tree' => $this->tree]); 99a26ec5edSGreg Roach $response = $module->handle($request); 100a26ec5edSGreg Roach self::assertSame(StatusCodeInterface::STATUS_MOVED_PERMANENTLY, $response->getStatusCode()); 101a26ec5edSGreg Roach self::assertStringContainsString('surname=MULLER', $response->getHeaderLine('Location')); 102a26ec5edSGreg Roach 1037dcc0dd1SGreg Roach $request = self::createRequest(RequestMethodInterface::METHOD_GET, ['surname' => 'MÜLLER'], [], [], ['tree' => $this->tree]); 104a26ec5edSGreg Roach $response = $module->handle($request); 1057dcc0dd1SGreg Roach self::assertSame(StatusCodeInterface::STATUS_MOVED_PERMANENTLY, $response->getStatusCode()); 1067dcc0dd1SGreg Roach self::assertStringContainsString('surname=MULLER', $response->getHeaderLine('Location')); 107b9fc687eSGreg Roach 1087dcc0dd1SGreg Roach I18N::init('de'); 1097dcc0dd1SGreg Roach $request = self::createRequest(RequestMethodInterface::METHOD_GET, ['surname' => 'MÜLLER'], [], [], ['tree' => $this->tree]); 110a26ec5edSGreg Roach $response = $module->handle($request); 1117dcc0dd1SGreg Roach self::assertSame(StatusCodeInterface::STATUS_MOVED_PERMANENTLY, $response->getStatusCode()); 1127dcc0dd1SGreg Roach self::assertStringContainsString('surname=MUELLER', $response->getHeaderLine('Location')); 1137dcc0dd1SGreg Roach } 1147dcc0dd1SGreg Roach 1157dcc0dd1SGreg Roach public function testCollationOfSurnames(): void 1167dcc0dd1SGreg Roach { 117a26ec5edSGreg Roach $module = new IndividualListModule(); 118a26ec5edSGreg Roach 1197dcc0dd1SGreg Roach $i1 = $this->tree->createIndividual("0 @@ INDI\n1 NAME /Muller/"); 1207dcc0dd1SGreg Roach $i2 = $this->tree->createIndividual("0 @@ INDI\n1 NAME /Müller/"); 1217dcc0dd1SGreg Roach $i3 = $this->tree->createIndividual("0 @@ INDI\n1 NAME /Mueller/"); 1227dcc0dd1SGreg Roach 1237dcc0dd1SGreg Roach I18N::init('en-US'); 1247dcc0dd1SGreg Roach $request = self::createRequest(RequestMethodInterface::METHOD_GET, ['surname' => 'MULLER'], [], [], ['tree' => $this->tree]); 125a26ec5edSGreg Roach $response = $module->handle($request); 1267dcc0dd1SGreg Roach $html = $response->getBody()->getContents(); 1277dcc0dd1SGreg Roach preg_match_all('/%2Fname%2Findividual%2F(X\d+)%2F/', $html, $matches); 1287dcc0dd1SGreg Roach self::assertEqualsCanonicalizing([$i1->xref(), $i2->xref()], $matches[1], 'English, so U should match U and Ü'); 1297dcc0dd1SGreg Roach 1307dcc0dd1SGreg Roach I18N::init('de'); 1317dcc0dd1SGreg Roach $request = self::createRequest(RequestMethodInterface::METHOD_GET, ['surname' => 'MULLER'], [], [], ['tree' => $this->tree]); 132a26ec5edSGreg Roach $response = $module->handle($request); 1337dcc0dd1SGreg Roach $html = $response->getBody()->getContents(); 1347dcc0dd1SGreg Roach preg_match_all('/%2Fname%2Findividual%2F(X\d+)%2F/', $html, $matches); 1357dcc0dd1SGreg Roach self::assertEqualsCanonicalizing([$i1->xref()], $matches[1], 'German, so U should only match U'); 1367dcc0dd1SGreg Roach 1377dcc0dd1SGreg Roach $request = self::createRequest(RequestMethodInterface::METHOD_GET, ['surname' => 'MUELLER'], [], [], ['tree' => $this->tree]); 138a26ec5edSGreg Roach $response = $module->handle($request); 1397dcc0dd1SGreg Roach $html = $response->getBody()->getContents(); 1407dcc0dd1SGreg Roach preg_match_all('/%2Fname%2Findividual%2F(X\d+)%2F/', $html, $matches); 1417dcc0dd1SGreg Roach self::assertEqualsCanonicalizing([$i2->xref(), $i3->xref()], $matches[1], 'German, so UE should also match Ü'); 1427dcc0dd1SGreg Roach } 1437dcc0dd1SGreg Roach 144a26ec5edSGreg Roach public function xtestUnknownVersusMissingSurname(): void 1457dcc0dd1SGreg Roach { 146a26ec5edSGreg Roach $module = new IndividualListModule(); 147a26ec5edSGreg Roach 1487dcc0dd1SGreg Roach $i1 = $this->tree->createIndividual("0 @@ INDI\n1 NAME John //"); 1497dcc0dd1SGreg Roach $i2 = $this->tree->createIndividual("0 @@ INDI\n1 NAME John"); 1507dcc0dd1SGreg Roach 1517dcc0dd1SGreg Roach $request = self::createRequest(RequestMethodInterface::METHOD_GET, ['alpha' => '@'], [], [], ['tree' => $this->tree]); 152a26ec5edSGreg Roach $response = $module->handle($request); 1537dcc0dd1SGreg Roach $html = $response->getBody()->getContents(); 1547dcc0dd1SGreg Roach preg_match_all('/%2Fname%2Findividual%2F(X\d+)%2F/', $html, $matches); 1557dcc0dd1SGreg Roach self::assertEqualsCanonicalizing([$i1->xref()], $matches[1]); 1567dcc0dd1SGreg Roach 1577dcc0dd1SGreg Roach $request = self::createRequest(RequestMethodInterface::METHOD_GET, ['alpha' => ','], [], [], ['tree' => $this->tree]); 158a26ec5edSGreg Roach $response = $module->handle($request); 1597dcc0dd1SGreg Roach $html = $response->getBody()->getContents(); 1607dcc0dd1SGreg Roach preg_match_all('/%2Fname%2Findividual%2F(X\d+)%2F/', $html, $matches); 1617dcc0dd1SGreg Roach self::assertEqualsCanonicalizing([$i2->xref()], $matches[1]); 1627dcc0dd1SGreg Roach } 1637dcc0dd1SGreg Roach 164a26ec5edSGreg Roach public function xtestAllSurnamesExcludesUnknownAndMissing(): void 1657dcc0dd1SGreg Roach { 166a26ec5edSGreg Roach $module = new IndividualListModule(); 167a26ec5edSGreg Roach 1687dcc0dd1SGreg Roach $i1 = $this->tree->createIndividual("0 @@ INDI\n1 NAME John /Black/"); 1697dcc0dd1SGreg Roach $i2 = $this->tree->createIndividual("0 @@ INDI\n1 NAME Mary /White/"); 1707dcc0dd1SGreg Roach $this->tree->createIndividual("0 @@ INDI\n1 NAME Peter //"); 1717dcc0dd1SGreg Roach $this->tree->createIndividual("0 @@ INDI\n1 NAME Paul"); 1727dcc0dd1SGreg Roach 1737dcc0dd1SGreg Roach $request = self::createRequest(RequestMethodInterface::METHOD_GET, ['show_all' => 'yes'], [], [], ['tree' => $this->tree]); 174a26ec5edSGreg Roach $response = $module->handle($request); 1757dcc0dd1SGreg Roach $html = $response->getBody()->getContents(); 1767dcc0dd1SGreg Roach preg_match_all('/individual-list&surname=([A-Z]+)/', $html, $matches); 1777dcc0dd1SGreg Roach self::assertEqualsCanonicalizing(['BLACK', 'WHITE'], $matches[1]); 1787dcc0dd1SGreg Roach 1797dcc0dd1SGreg Roach $request = self::createRequest(RequestMethodInterface::METHOD_GET, ['show_all' => 'yes', 'show' => 'indi'], [], [], ['tree' => $this->tree]); 180a26ec5edSGreg Roach $response = $module->handle($request); 1817dcc0dd1SGreg Roach $html = $response->getBody()->getContents(); 1827dcc0dd1SGreg Roach preg_match_all('/%2Fname%2Findividual%2F(X\d+)%2F/', $html, $matches); 1837dcc0dd1SGreg Roach self::assertEqualsCanonicalizing([$i1->xref(), $i2->xref()], $matches[1]); 1847dcc0dd1SGreg Roach } 1857dcc0dd1SGreg Roach 186a26ec5edSGreg Roach public function xtestSurnameInitial(): void 1877dcc0dd1SGreg Roach { 188a26ec5edSGreg Roach $module = new IndividualListModule(); 189a26ec5edSGreg Roach 1907dcc0dd1SGreg Roach $i1 = $this->tree->createIndividual("0 @@ INDI\n1 NAME John /Black/"); 1917dcc0dd1SGreg Roach $i2 = $this->tree->createIndividual("0 @@ INDI\n1 NAME Mary /Brown/"); 1927dcc0dd1SGreg Roach $this->tree->createIndividual("0 @@ INDI\n1 NAME Peter /White/"); 1937dcc0dd1SGreg Roach $this->tree->createIndividual("0 @@ INDI\n1 NAME Paul /Green/"); 1947dcc0dd1SGreg Roach 1957dcc0dd1SGreg Roach $request = self::createRequest(RequestMethodInterface::METHOD_GET, ['alpha' => 'B'], [], [], ['tree' => $this->tree]); 196a26ec5edSGreg Roach $response = $module->handle($request); 1977dcc0dd1SGreg Roach $html = $response->getBody()->getContents(); 1987dcc0dd1SGreg Roach preg_match_all('/individual-list&surname=([A-Z]+)/', $html, $matches); 1997dcc0dd1SGreg Roach self::assertEqualsCanonicalizing(['BLACK', 'BROWN'], $matches[1]); 2007dcc0dd1SGreg Roach 2017dcc0dd1SGreg Roach $request = self::createRequest(RequestMethodInterface::METHOD_GET, ['alpha' => 'B', 'show' => 'indi'], [], [], ['tree' => $this->tree]); 202a26ec5edSGreg Roach $response = $module->handle($request); 2037dcc0dd1SGreg Roach $html = $response->getBody()->getContents(); 2047dcc0dd1SGreg Roach preg_match_all('/%2Fname%2Findividual%2F(X\d+)%2F/', $html, $matches); 2057dcc0dd1SGreg Roach self::assertEqualsCanonicalizing([$i1->xref(), $i2->xref()], $matches[1]); 206e9e85398SGreg Roach } 207e9e85398SGreg Roach} 208