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