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; 24*7dcc0dd1SGreg Roachuse Fisharebest\Webtrees\Contracts\UserInterface; 2587cca37cSGreg Roachuse Fisharebest\Webtrees\Module\IndividualListModule; 26*7dcc0dd1SGreg Roachuse Fisharebest\Webtrees\Services\GedcomImportService; 27*7dcc0dd1SGreg Roachuse Fisharebest\Webtrees\Services\TreeService; 28*7dcc0dd1SGreg Roachuse Fisharebest\Webtrees\Services\UserService; 29*7dcc0dd1SGreg Roach 30*7dcc0dd1SGreg Roachuse function array_map; 31*7dcc0dd1SGreg Roachuse function preg_match_all; 32b9fc687eSGreg Roach 33e9e85398SGreg Roach/** 34b9fc687eSGreg Roach * Test the individual lists. 35e9e85398SGreg Roach * 36e9e85398SGreg Roach * @coversNothing 37e9e85398SGreg Roach */ 38e5a6b4d4SGreg Roachclass IndividualListTest extends TestCase 39e9e85398SGreg Roach{ 40cd94ca66SGreg Roach protected static bool $uses_database = true; 41e9e85398SGreg Roach 42*7dcc0dd1SGreg Roach private IndividualListModule $module; 43*7dcc0dd1SGreg Roach private Tree $tree; 44*7dcc0dd1SGreg Roach private User $user; 45*7dcc0dd1SGreg Roach 46*7dcc0dd1SGreg Roach public function setUp(): void 47*7dcc0dd1SGreg Roach { 48*7dcc0dd1SGreg Roach I18N::init('en-US'); 49*7dcc0dd1SGreg Roach 50*7dcc0dd1SGreg Roach $this->module = new IndividualListModule(); 51*7dcc0dd1SGreg Roach $user_service = new UserService(); 52*7dcc0dd1SGreg Roach $tree_service = new TreeService(new GedcomImportService()); 53*7dcc0dd1SGreg Roach $this->tree = $tree_service->create('name', 'title'); 54*7dcc0dd1SGreg Roach $this->user = $user_service->create('user', 'User', 'user@example.com', 'secret'); 55*7dcc0dd1SGreg Roach $this->user->setPreference(UserInterface::PREF_IS_ADMINISTRATOR, '1'); 56*7dcc0dd1SGreg Roach $this->user->setPreference(UserInterface::PREF_AUTO_ACCEPT_EDITS, '1'); 57*7dcc0dd1SGreg Roach Auth::login($this->user); 58*7dcc0dd1SGreg Roach // The default "John Doe" individual will confuse the test results... 59*7dcc0dd1SGreg Roach Registry::individualFactory()->make('X1', $this->tree)->deleteRecord(); 60*7dcc0dd1SGreg Roach } 61*7dcc0dd1SGreg Roach 62*7dcc0dd1SGreg Roach public function tearDown(): void 63*7dcc0dd1SGreg Roach { 64*7dcc0dd1SGreg Roach $tree_service = new TreeService(new GedcomImportService()); 65*7dcc0dd1SGreg Roach $tree_service->delete($this->tree); 66*7dcc0dd1SGreg Roach 67*7dcc0dd1SGreg Roach $user_service = new UserService(); 68*7dcc0dd1SGreg Roach $user_service->delete($this->user); 69*7dcc0dd1SGreg Roach } 70*7dcc0dd1SGreg Roach 71e9e85398SGreg Roach /** 725e933c21SGreg Roach * @covers \Fisharebest\Webtrees\Module\IndividualListModule 73e9e85398SGreg Roach */ 74*7dcc0dd1SGreg Roach public function testCollationOfInitials(): void 75e9e85398SGreg Roach { 76*7dcc0dd1SGreg Roach $this->tree->createIndividual("0 @@ INDI\n1 NAME /Âaa/"); 77*7dcc0dd1SGreg Roach $this->tree->createIndividual("0 @@ INDI\n1 NAME /aaa/"); 78*7dcc0dd1SGreg Roach $this->tree->createIndividual("0 @@ INDI\n1 NAME /Ååå/"); 79*7dcc0dd1SGreg Roach $this->tree->createIndividual("0 @@ INDI\n1 NAME /æææ/"); 80*7dcc0dd1SGreg Roach $this->tree->createIndividual("0 @@ INDI\n1 NAME /Caa/"); 81*7dcc0dd1SGreg Roach $this->tree->createIndividual("0 @@ INDI\n1 NAME /Css/"); 82*7dcc0dd1SGreg Roach $this->tree->createIndividual("0 @@ INDI\n1 NAME /Dza/"); 83b9fc687eSGreg Roach 84*7dcc0dd1SGreg Roach I18N::init('en-US'); 85*7dcc0dd1SGreg Roach $request = self::createRequest(RequestMethodInterface::METHOD_GET, [], [], [], ['tree' => $this->tree]); 86*7dcc0dd1SGreg Roach $response = $this->module->handle($request); 87*7dcc0dd1SGreg Roach $html = $response->getBody()->getContents(); 88*7dcc0dd1SGreg Roach preg_match_all('/%2Findividual-list&alpha=([^"&]+)/', $html, $matches); 89*7dcc0dd1SGreg Roach self::assertEquals(['A', 'C', 'D', 'Æ'], array_map(rawurldecode(...), $matches[1])); 90b9fc687eSGreg Roach 91*7dcc0dd1SGreg Roach I18N::init('sv'); 92*7dcc0dd1SGreg Roach $request = self::createRequest(RequestMethodInterface::METHOD_GET, [], [], [], ['tree' => $this->tree]); 93*7dcc0dd1SGreg Roach $response = $this->module->handle($request); 94*7dcc0dd1SGreg Roach $html = $response->getBody()->getContents(); 95*7dcc0dd1SGreg Roach preg_match_all('/%2Findividual-list&alpha=([^"&]+)/', $html, $matches); 96*7dcc0dd1SGreg Roach self::assertEquals(['A', 'C', 'D', 'Å', 'Æ'], array_map(rawurldecode(...), $matches[1])); 97b9fc687eSGreg Roach 98*7dcc0dd1SGreg Roach I18N::init('hu'); 99*7dcc0dd1SGreg Roach $request = self::createRequest(RequestMethodInterface::METHOD_GET, [], [], [], ['tree' => $this->tree]); 100*7dcc0dd1SGreg Roach $response = $this->module->handle($request); 101*7dcc0dd1SGreg Roach $html = $response->getBody()->getContents(); 102*7dcc0dd1SGreg Roach preg_match_all('/%2Findividual-list&alpha=([^"&]+)/', $html, $matches); 103*7dcc0dd1SGreg Roach self::assertEquals(['A', 'C', 'CS', 'DZ', 'Æ'], array_map(rawurldecode(...), $matches[1])); 104*7dcc0dd1SGreg Roach } 105a67cd39fSGreg Roach 106*7dcc0dd1SGreg Roach /** 107*7dcc0dd1SGreg Roach * @covers \Fisharebest\Webtrees\Module\IndividualListModule 108*7dcc0dd1SGreg Roach */ 109*7dcc0dd1SGreg Roach public function testRedirectToCanonicalSurname(): void 110*7dcc0dd1SGreg Roach { 111*7dcc0dd1SGreg Roach I18N::init('en-US'); 112*7dcc0dd1SGreg Roach $request = self::createRequest(RequestMethodInterface::METHOD_GET, ['surname' => 'MÜLLER'], [], [], ['tree' => $this->tree]); 113*7dcc0dd1SGreg Roach $response = $this->module->handle($request); 114*7dcc0dd1SGreg Roach self::assertSame(StatusCodeInterface::STATUS_MOVED_PERMANENTLY, $response->getStatusCode()); 115*7dcc0dd1SGreg Roach self::assertStringContainsString('surname=MULLER', $response->getHeaderLine('Location')); 116b9fc687eSGreg Roach 117*7dcc0dd1SGreg Roach I18N::init('de'); 118*7dcc0dd1SGreg Roach $request = self::createRequest(RequestMethodInterface::METHOD_GET, ['surname' => 'MÜLLER'], [], [], ['tree' => $this->tree]); 119*7dcc0dd1SGreg Roach $response = $this->module->handle($request); 120*7dcc0dd1SGreg Roach self::assertSame(StatusCodeInterface::STATUS_MOVED_PERMANENTLY, $response->getStatusCode()); 121*7dcc0dd1SGreg Roach self::assertStringContainsString('surname=MUELLER', $response->getHeaderLine('Location')); 122*7dcc0dd1SGreg Roach } 123*7dcc0dd1SGreg Roach 124*7dcc0dd1SGreg Roach /** 125*7dcc0dd1SGreg Roach * @covers \Fisharebest\Webtrees\Module\IndividualListModule 126*7dcc0dd1SGreg Roach */ 127*7dcc0dd1SGreg Roach public function testCollationOfSurnames(): void 128*7dcc0dd1SGreg Roach { 129*7dcc0dd1SGreg Roach $i1 = $this->tree->createIndividual("0 @@ INDI\n1 NAME /Muller/"); 130*7dcc0dd1SGreg Roach $i2 = $this->tree->createIndividual("0 @@ INDI\n1 NAME /Müller/"); 131*7dcc0dd1SGreg Roach $i3 = $this->tree->createIndividual("0 @@ INDI\n1 NAME /Mueller/"); 132*7dcc0dd1SGreg Roach 133*7dcc0dd1SGreg Roach I18N::init('en-US'); 134*7dcc0dd1SGreg Roach $request = self::createRequest(RequestMethodInterface::METHOD_GET, ['surname' => 'MULLER'], [], [], ['tree' => $this->tree]); 135*7dcc0dd1SGreg Roach $response = $this->module->handle($request); 136*7dcc0dd1SGreg Roach $html = $response->getBody()->getContents(); 137*7dcc0dd1SGreg Roach preg_match_all('/%2Fname%2Findividual%2F(X\d+)%2F/', $html, $matches); 138*7dcc0dd1SGreg Roach self::assertEqualsCanonicalizing([$i1->xref(), $i2->xref()], $matches[1], 'English, so U should match U and Ü'); 139*7dcc0dd1SGreg Roach 140*7dcc0dd1SGreg Roach I18N::init('de'); 141*7dcc0dd1SGreg Roach $request = self::createRequest(RequestMethodInterface::METHOD_GET, ['surname' => 'MULLER'], [], [], ['tree' => $this->tree]); 142*7dcc0dd1SGreg Roach $response = $this->module->handle($request); 143*7dcc0dd1SGreg Roach $html = $response->getBody()->getContents(); 144*7dcc0dd1SGreg Roach preg_match_all('/%2Fname%2Findividual%2F(X\d+)%2F/', $html, $matches); 145*7dcc0dd1SGreg Roach self::assertEqualsCanonicalizing([$i1->xref()], $matches[1], 'German, so U should only match U'); 146*7dcc0dd1SGreg Roach 147*7dcc0dd1SGreg Roach $request = self::createRequest(RequestMethodInterface::METHOD_GET, ['surname' => 'MUELLER'], [], [], ['tree' => $this->tree]); 148*7dcc0dd1SGreg Roach $response = $this->module->handle($request); 149*7dcc0dd1SGreg Roach $html = $response->getBody()->getContents(); 150*7dcc0dd1SGreg Roach preg_match_all('/%2Fname%2Findividual%2F(X\d+)%2F/', $html, $matches); 151*7dcc0dd1SGreg Roach self::assertEqualsCanonicalizing([$i2->xref(), $i3->xref()], $matches[1], 'German, so UE should also match Ü'); 152*7dcc0dd1SGreg Roach } 153*7dcc0dd1SGreg Roach 154*7dcc0dd1SGreg Roach /** 155*7dcc0dd1SGreg Roach * @covers \Fisharebest\Webtrees\Module\IndividualListModule 156*7dcc0dd1SGreg Roach */ 157*7dcc0dd1SGreg Roach public function testUnknownVersusMissingSurname(): void 158*7dcc0dd1SGreg Roach { 159*7dcc0dd1SGreg Roach $i1 = $this->tree->createIndividual("0 @@ INDI\n1 NAME John //"); 160*7dcc0dd1SGreg Roach $i2 = $this->tree->createIndividual("0 @@ INDI\n1 NAME John"); 161*7dcc0dd1SGreg Roach 162*7dcc0dd1SGreg Roach $request = self::createRequest(RequestMethodInterface::METHOD_GET, ['alpha' => '@'], [], [], ['tree' => $this->tree]); 163*7dcc0dd1SGreg Roach $response = $this->module->handle($request); 164*7dcc0dd1SGreg Roach $html = $response->getBody()->getContents(); 165*7dcc0dd1SGreg Roach preg_match_all('/%2Fname%2Findividual%2F(X\d+)%2F/', $html, $matches); 166*7dcc0dd1SGreg Roach self::assertEqualsCanonicalizing([$i1->xref()], $matches[1]); 167*7dcc0dd1SGreg Roach 168*7dcc0dd1SGreg Roach $request = self::createRequest(RequestMethodInterface::METHOD_GET, ['alpha' => ','], [], [], ['tree' => $this->tree]); 169*7dcc0dd1SGreg Roach $response = $this->module->handle($request); 170*7dcc0dd1SGreg Roach $html = $response->getBody()->getContents(); 171*7dcc0dd1SGreg Roach preg_match_all('/%2Fname%2Findividual%2F(X\d+)%2F/', $html, $matches); 172*7dcc0dd1SGreg Roach self::assertEqualsCanonicalizing([$i2->xref()], $matches[1]); 173*7dcc0dd1SGreg Roach } 174*7dcc0dd1SGreg Roach 175*7dcc0dd1SGreg Roach /** 176*7dcc0dd1SGreg Roach * @covers \Fisharebest\Webtrees\Module\IndividualListModule 177*7dcc0dd1SGreg Roach */ 178*7dcc0dd1SGreg Roach public function testAllSurnamesExcludesUnknownAndMissing(): void 179*7dcc0dd1SGreg Roach { 180*7dcc0dd1SGreg Roach $i1 = $this->tree->createIndividual("0 @@ INDI\n1 NAME John /Black/"); 181*7dcc0dd1SGreg Roach $i2 = $this->tree->createIndividual("0 @@ INDI\n1 NAME Mary /White/"); 182*7dcc0dd1SGreg Roach $this->tree->createIndividual("0 @@ INDI\n1 NAME Peter //"); 183*7dcc0dd1SGreg Roach $this->tree->createIndividual("0 @@ INDI\n1 NAME Paul"); 184*7dcc0dd1SGreg Roach 185*7dcc0dd1SGreg Roach $request = self::createRequest(RequestMethodInterface::METHOD_GET, ['show_all' => 'yes'], [], [], ['tree' => $this->tree]); 186*7dcc0dd1SGreg Roach $response = $this->module->handle($request); 187*7dcc0dd1SGreg Roach $html = $response->getBody()->getContents(); 188*7dcc0dd1SGreg Roach preg_match_all('/individual-list&surname=([A-Z]+)/', $html, $matches); 189*7dcc0dd1SGreg Roach self::assertEqualsCanonicalizing(['BLACK', 'WHITE'], $matches[1]); 190*7dcc0dd1SGreg Roach 191*7dcc0dd1SGreg Roach $request = self::createRequest(RequestMethodInterface::METHOD_GET, ['show_all' => 'yes', 'show' => 'indi'], [], [], ['tree' => $this->tree]); 192*7dcc0dd1SGreg Roach $response = $this->module->handle($request); 193*7dcc0dd1SGreg Roach $html = $response->getBody()->getContents(); 194*7dcc0dd1SGreg Roach preg_match_all('/%2Fname%2Findividual%2F(X\d+)%2F/', $html, $matches); 195*7dcc0dd1SGreg Roach self::assertEqualsCanonicalizing([$i1->xref(), $i2->xref()], $matches[1]); 196*7dcc0dd1SGreg Roach } 197*7dcc0dd1SGreg Roach 198*7dcc0dd1SGreg Roach /** 199*7dcc0dd1SGreg Roach * @covers \Fisharebest\Webtrees\Module\IndividualListModule 200*7dcc0dd1SGreg Roach */ 201*7dcc0dd1SGreg Roach public function testSurnameInitial(): void 202*7dcc0dd1SGreg Roach { 203*7dcc0dd1SGreg Roach $i1 = $this->tree->createIndividual("0 @@ INDI\n1 NAME John /Black/"); 204*7dcc0dd1SGreg Roach $i2 = $this->tree->createIndividual("0 @@ INDI\n1 NAME Mary /Brown/"); 205*7dcc0dd1SGreg Roach $this->tree->createIndividual("0 @@ INDI\n1 NAME Peter /White/"); 206*7dcc0dd1SGreg Roach $this->tree->createIndividual("0 @@ INDI\n1 NAME Paul /Green/"); 207*7dcc0dd1SGreg Roach 208*7dcc0dd1SGreg Roach $request = self::createRequest(RequestMethodInterface::METHOD_GET, ['alpha' => 'B'], [], [], ['tree' => $this->tree]); 209*7dcc0dd1SGreg Roach $response = $this->module->handle($request); 210*7dcc0dd1SGreg Roach $html = $response->getBody()->getContents(); 211*7dcc0dd1SGreg Roach preg_match_all('/individual-list&surname=([A-Z]+)/', $html, $matches); 212*7dcc0dd1SGreg Roach self::assertEqualsCanonicalizing(['BLACK', 'BROWN'], $matches[1]); 213*7dcc0dd1SGreg Roach 214*7dcc0dd1SGreg Roach $request = self::createRequest(RequestMethodInterface::METHOD_GET, ['alpha' => 'B', 'show' => 'indi'], [], [], ['tree' => $this->tree]); 215*7dcc0dd1SGreg Roach $response = $this->module->handle($request); 216*7dcc0dd1SGreg Roach $html = $response->getBody()->getContents(); 217*7dcc0dd1SGreg Roach preg_match_all('/%2Fname%2Findividual%2F(X\d+)%2F/', $html, $matches); 218*7dcc0dd1SGreg Roach self::assertEqualsCanonicalizing([$i1->xref(), $i2->xref()], $matches[1]); 219e9e85398SGreg Roach } 220e9e85398SGreg Roach} 221