xref: /webtrees/tests/feature/IndividualListTest.php (revision a26ec5ede3c2ecdb45b783755ede967935aaecba)
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&amp;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&amp;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&amp;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&amp;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&amp;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