1<?php 2 3/** 4 * webtrees: online genealogy 5 * Copyright (C) 2023 webtrees development team 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation, either version 3 of the License, or 9 * (at your option) any later version. 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * You should have received a copy of the GNU General Public License 15 * along with this program. If not, see <https://www.gnu.org/licenses/>. 16 */ 17 18declare(strict_types=1); 19 20namespace Fisharebest\Webtrees; 21 22use Fig\Http\Message\RequestMethodInterface; 23use Fig\Http\Message\StatusCodeInterface; 24use Fisharebest\Webtrees\Contracts\UserInterface; 25use Fisharebest\Webtrees\Module\IndividualListModule; 26use Fisharebest\Webtrees\Services\GedcomImportService; 27use Fisharebest\Webtrees\Services\TreeService; 28use Fisharebest\Webtrees\Services\UserService; 29 30use function array_map; 31use function preg_match_all; 32 33/** 34 * Test the individual lists. 35 * 36 * @coversNothing 37 */ 38class IndividualListTest extends TestCase 39{ 40 protected static bool $uses_database = true; 41 42 private Tree $tree; 43 private User $user; 44 45 public function setUp(): void 46 { 47 parent::setUp(); 48 49 I18N::init('en-US'); 50 51 $user_service = new UserService(); 52 $tree_service = new TreeService(new GedcomImportService()); 53 $this->tree = $tree_service->create('name', 'title'); 54 $this->user = $user_service->create('user', 'User', 'user@example.com', 'secret'); 55 $this->user->setPreference(UserInterface::PREF_IS_ADMINISTRATOR, '1'); 56 $this->user->setPreference(UserInterface::PREF_AUTO_ACCEPT_EDITS, '1'); 57 Auth::login($this->user); 58 // The default "John Doe" individual will confuse the test results... 59 Registry::individualFactory()->make('X1', $this->tree)->deleteRecord(); 60 } 61 62 /** 63 * @covers \Fisharebest\Webtrees\Module\IndividualListModule 64 */ 65 public function testCollationOfInitials(): void 66 { 67 $module = new IndividualListModule(); 68 69 $this->tree->createIndividual("0 @@ INDI\n1 NAME /Âaa/"); 70 $this->tree->createIndividual("0 @@ INDI\n1 NAME /aaa/"); 71 $this->tree->createIndividual("0 @@ INDI\n1 NAME /Ååå/"); 72 $this->tree->createIndividual("0 @@ INDI\n1 NAME /æææ/"); 73 $this->tree->createIndividual("0 @@ INDI\n1 NAME /Caa/"); 74 $this->tree->createIndividual("0 @@ INDI\n1 NAME /Css/"); 75 $this->tree->createIndividual("0 @@ INDI\n1 NAME /Dza/"); 76 77 I18N::init('en-US'); 78 $request = self::createRequest(RequestMethodInterface::METHOD_GET, [], [], [], ['tree' => $this->tree]); 79 $response = $module->handle($request); 80 $html = $response->getBody()->getContents(); 81 preg_match_all('/%2Findividual-list&alpha=([^"&]+)/', $html, $matches); 82 self::assertEquals(['A', 'C', 'D', 'Æ'], array_map(rawurldecode(...), $matches[1])); 83 84 I18N::init('sv'); 85 $request = self::createRequest(RequestMethodInterface::METHOD_GET, [], [], [], ['tree' => $this->tree]); 86 $response = $module->handle($request); 87 $html = $response->getBody()->getContents(); 88 preg_match_all('/%2Findividual-list&alpha=([^"&]+)/', $html, $matches); 89 self::assertEquals(['A', 'C', 'D', 'Å', 'Æ'], array_map(rawurldecode(...), $matches[1])); 90 91 I18N::init('hu'); 92 $request = self::createRequest(RequestMethodInterface::METHOD_GET, [], [], [], ['tree' => $this->tree]); 93 $response = $module->handle($request); 94 $html = $response->getBody()->getContents(); 95 preg_match_all('/%2Findividual-list&alpha=([^"&]+)/', $html, $matches); 96 self::assertEquals(['A', 'C', 'CS', 'DZ', 'Æ'], array_map(rawurldecode(...), $matches[1])); 97 } 98 99 /** 100 * @covers \Fisharebest\Webtrees\Module\IndividualListModule 101 */ 102 public function xtestRedirectToCanonicalSurname(): void 103 { 104 $module = new IndividualListModule(); 105 106 I18N::init('en-US'); 107 $request = self::createRequest(RequestMethodInterface::METHOD_GET, ['surname' => 'Muller'], [], [], ['tree' => $this->tree]); 108 $response = $module->handle($request); 109 self::assertSame(StatusCodeInterface::STATUS_MOVED_PERMANENTLY, $response->getStatusCode()); 110 self::assertStringContainsString('surname=MULLER', $response->getHeaderLine('Location')); 111 112 $request = self::createRequest(RequestMethodInterface::METHOD_GET, ['surname' => 'MÜLLER'], [], [], ['tree' => $this->tree]); 113 $response = $module->handle($request); 114 self::assertSame(StatusCodeInterface::STATUS_MOVED_PERMANENTLY, $response->getStatusCode()); 115 self::assertStringContainsString('surname=MULLER', $response->getHeaderLine('Location')); 116 117 I18N::init('de'); 118 $request = self::createRequest(RequestMethodInterface::METHOD_GET, ['surname' => 'MÜLLER'], [], [], ['tree' => $this->tree]); 119 $response = $module->handle($request); 120 self::assertSame(StatusCodeInterface::STATUS_MOVED_PERMANENTLY, $response->getStatusCode()); 121 self::assertStringContainsString('surname=MUELLER', $response->getHeaderLine('Location')); 122 } 123 124 /** 125 * @covers \Fisharebest\Webtrees\Module\IndividualListModule 126 */ 127 public function testCollationOfSurnames(): void 128 { 129 $module = new IndividualListModule(); 130 131 $i1 = $this->tree->createIndividual("0 @@ INDI\n1 NAME /Muller/"); 132 $i2 = $this->tree->createIndividual("0 @@ INDI\n1 NAME /Müller/"); 133 $i3 = $this->tree->createIndividual("0 @@ INDI\n1 NAME /Mueller/"); 134 135 I18N::init('en-US'); 136 $request = self::createRequest(RequestMethodInterface::METHOD_GET, ['surname' => 'MULLER'], [], [], ['tree' => $this->tree]); 137 $response = $module->handle($request); 138 $html = $response->getBody()->getContents(); 139 preg_match_all('/%2Fname%2Findividual%2F(X\d+)%2F/', $html, $matches); 140 self::assertEqualsCanonicalizing([$i1->xref(), $i2->xref()], $matches[1], 'English, so U should match U and Ü'); 141 142 I18N::init('de'); 143 $request = self::createRequest(RequestMethodInterface::METHOD_GET, ['surname' => 'MULLER'], [], [], ['tree' => $this->tree]); 144 $response = $module->handle($request); 145 $html = $response->getBody()->getContents(); 146 preg_match_all('/%2Fname%2Findividual%2F(X\d+)%2F/', $html, $matches); 147 self::assertEqualsCanonicalizing([$i1->xref()], $matches[1], 'German, so U should only match U'); 148 149 $request = self::createRequest(RequestMethodInterface::METHOD_GET, ['surname' => 'MUELLER'], [], [], ['tree' => $this->tree]); 150 $response = $module->handle($request); 151 $html = $response->getBody()->getContents(); 152 preg_match_all('/%2Fname%2Findividual%2F(X\d+)%2F/', $html, $matches); 153 self::assertEqualsCanonicalizing([$i2->xref(), $i3->xref()], $matches[1], 'German, so UE should also match Ü'); 154 } 155 156 /** 157 * @covers \Fisharebest\Webtrees\Module\IndividualListModule 158 */ 159 public function xtestUnknownVersusMissingSurname(): void 160 { 161 $module = new IndividualListModule(); 162 163 $i1 = $this->tree->createIndividual("0 @@ INDI\n1 NAME John //"); 164 $i2 = $this->tree->createIndividual("0 @@ INDI\n1 NAME John"); 165 166 $request = self::createRequest(RequestMethodInterface::METHOD_GET, ['alpha' => '@'], [], [], ['tree' => $this->tree]); 167 $response = $module->handle($request); 168 $html = $response->getBody()->getContents(); 169 preg_match_all('/%2Fname%2Findividual%2F(X\d+)%2F/', $html, $matches); 170 self::assertEqualsCanonicalizing([$i1->xref()], $matches[1]); 171 172 $request = self::createRequest(RequestMethodInterface::METHOD_GET, ['alpha' => ','], [], [], ['tree' => $this->tree]); 173 $response = $module->handle($request); 174 $html = $response->getBody()->getContents(); 175 preg_match_all('/%2Fname%2Findividual%2F(X\d+)%2F/', $html, $matches); 176 self::assertEqualsCanonicalizing([$i2->xref()], $matches[1]); 177 } 178 179 /** 180 * @covers \Fisharebest\Webtrees\Module\IndividualListModule 181 */ 182 public function xtestAllSurnamesExcludesUnknownAndMissing(): void 183 { 184 $module = new IndividualListModule(); 185 186 $i1 = $this->tree->createIndividual("0 @@ INDI\n1 NAME John /Black/"); 187 $i2 = $this->tree->createIndividual("0 @@ INDI\n1 NAME Mary /White/"); 188 $this->tree->createIndividual("0 @@ INDI\n1 NAME Peter //"); 189 $this->tree->createIndividual("0 @@ INDI\n1 NAME Paul"); 190 191 $request = self::createRequest(RequestMethodInterface::METHOD_GET, ['show_all' => 'yes'], [], [], ['tree' => $this->tree]); 192 $response = $module->handle($request); 193 $html = $response->getBody()->getContents(); 194 preg_match_all('/individual-list&surname=([A-Z]+)/', $html, $matches); 195 self::assertEqualsCanonicalizing(['BLACK', 'WHITE'], $matches[1]); 196 197 $request = self::createRequest(RequestMethodInterface::METHOD_GET, ['show_all' => 'yes', 'show' => 'indi'], [], [], ['tree' => $this->tree]); 198 $response = $module->handle($request); 199 $html = $response->getBody()->getContents(); 200 preg_match_all('/%2Fname%2Findividual%2F(X\d+)%2F/', $html, $matches); 201 self::assertEqualsCanonicalizing([$i1->xref(), $i2->xref()], $matches[1]); 202 } 203 204 /** 205 * @covers \Fisharebest\Webtrees\Module\IndividualListModule 206 */ 207 public function xtestSurnameInitial(): void 208 { 209 $module = new IndividualListModule(); 210 211 $i1 = $this->tree->createIndividual("0 @@ INDI\n1 NAME John /Black/"); 212 $i2 = $this->tree->createIndividual("0 @@ INDI\n1 NAME Mary /Brown/"); 213 $this->tree->createIndividual("0 @@ INDI\n1 NAME Peter /White/"); 214 $this->tree->createIndividual("0 @@ INDI\n1 NAME Paul /Green/"); 215 216 $request = self::createRequest(RequestMethodInterface::METHOD_GET, ['alpha' => 'B'], [], [], ['tree' => $this->tree]); 217 $response = $module->handle($request); 218 $html = $response->getBody()->getContents(); 219 preg_match_all('/individual-list&surname=([A-Z]+)/', $html, $matches); 220 self::assertEqualsCanonicalizing(['BLACK', 'BROWN'], $matches[1]); 221 222 $request = self::createRequest(RequestMethodInterface::METHOD_GET, ['alpha' => 'B', 'show' => 'indi'], [], [], ['tree' => $this->tree]); 223 $response = $module->handle($request); 224 $html = $response->getBody()->getContents(); 225 preg_match_all('/%2Fname%2Findividual%2F(X\d+)%2F/', $html, $matches); 226 self::assertEqualsCanonicalizing([$i1->xref(), $i2->xref()], $matches[1]); 227 } 228} 229