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