xref: /webtrees/tests/feature/IndividualListTest.php (revision 99059edd63588c85305bca5fa411fae3e8a3bb5d)
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 IndividualListModule $module;
43    private Tree $tree;
44    private User $user;
45
46    public function setUp(): void
47    {
48        I18N::init('en-US');
49
50        $this->module = new IndividualListModule();
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    public function tearDown(): void
63    {
64        $tree_service = new TreeService(new GedcomImportService());
65        $tree_service->delete($this->tree);
66
67        $user_service = new UserService();
68        $user_service->delete($this->user);
69    }
70
71    /**
72     * @covers \Fisharebest\Webtrees\Module\IndividualListModule
73     */
74    public function testCollationOfInitials(): void
75    {
76        $this->tree->createIndividual("0 @@ INDI\n1 NAME /Âaa/");
77        $this->tree->createIndividual("0 @@ INDI\n1 NAME /aaa/");
78        $this->tree->createIndividual("0 @@ INDI\n1 NAME /Ååå/");
79        $this->tree->createIndividual("0 @@ INDI\n1 NAME /æææ/");
80        $this->tree->createIndividual("0 @@ INDI\n1 NAME /Caa/");
81        $this->tree->createIndividual("0 @@ INDI\n1 NAME /Css/");
82        $this->tree->createIndividual("0 @@ INDI\n1 NAME /Dza/");
83
84        I18N::init('en-US');
85        $request  = self::createRequest(RequestMethodInterface::METHOD_GET, [], [], [], ['tree' => $this->tree]);
86        $response = $this->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('sv');
92        $request  = self::createRequest(RequestMethodInterface::METHOD_GET, [], [], [], ['tree' => $this->tree]);
93        $response = $this->module->handle($request);
94        $html     = $response->getBody()->getContents();
95        preg_match_all('/%2Findividual-list&amp;alpha=([^"&]+)/', $html, $matches);
96        self::assertEquals(['A', 'C', 'D', 'Å', 'Æ'], array_map(rawurldecode(...), $matches[1]));
97
98        I18N::init('hu');
99        $request  = self::createRequest(RequestMethodInterface::METHOD_GET, [], [], [], ['tree' => $this->tree]);
100        $response = $this->module->handle($request);
101        $html     = $response->getBody()->getContents();
102        preg_match_all('/%2Findividual-list&amp;alpha=([^"&]+)/', $html, $matches);
103        self::assertEquals(['A', 'C', 'CS', 'DZ', 'Æ'], array_map(rawurldecode(...), $matches[1]));
104    }
105
106    /**
107     * @covers \Fisharebest\Webtrees\Module\IndividualListModule
108     */
109    public function testRedirectToCanonicalSurname(): void
110    {
111        I18N::init('en-US');
112        $request  = self::createRequest(RequestMethodInterface::METHOD_GET, ['surname' => 'MÜLLER'], [], [], ['tree' => $this->tree]);
113        $response = $this->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 = $this->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        $i1 = $this->tree->createIndividual("0 @@ INDI\n1 NAME /Muller/");
130        $i2 = $this->tree->createIndividual("0 @@ INDI\n1 NAME /Müller/");
131        $i3 = $this->tree->createIndividual("0 @@ INDI\n1 NAME /Mueller/");
132
133        I18N::init('en-US');
134        $request  = self::createRequest(RequestMethodInterface::METHOD_GET, ['surname' => 'MULLER'], [], [], ['tree' => $this->tree]);
135        $response = $this->module->handle($request);
136        $html     = $response->getBody()->getContents();
137        preg_match_all('/%2Fname%2Findividual%2F(X\d+)%2F/', $html, $matches);
138        self::assertEqualsCanonicalizing([$i1->xref(), $i2->xref()], $matches[1], 'English, so U should match U and Ü');
139
140        I18N::init('de');
141        $request  = self::createRequest(RequestMethodInterface::METHOD_GET, ['surname' => 'MULLER'], [], [], ['tree' => $this->tree]);
142        $response = $this->module->handle($request);
143        $html     = $response->getBody()->getContents();
144        preg_match_all('/%2Fname%2Findividual%2F(X\d+)%2F/', $html, $matches);
145        self::assertEqualsCanonicalizing([$i1->xref()], $matches[1], 'German, so U should only match U');
146
147        $request  = self::createRequest(RequestMethodInterface::METHOD_GET, ['surname' => 'MUELLER'], [], [], ['tree' => $this->tree]);
148        $response = $this->module->handle($request);
149        $html     = $response->getBody()->getContents();
150        preg_match_all('/%2Fname%2Findividual%2F(X\d+)%2F/', $html, $matches);
151        self::assertEqualsCanonicalizing([$i2->xref(), $i3->xref()], $matches[1], 'German, so UE should also match Ü');
152    }
153
154    /**
155     * @covers \Fisharebest\Webtrees\Module\IndividualListModule
156     */
157    public function testUnknownVersusMissingSurname(): void
158    {
159        $i1 = $this->tree->createIndividual("0 @@ INDI\n1 NAME John //");
160        $i2 = $this->tree->createIndividual("0 @@ INDI\n1 NAME John");
161
162        $request  = self::createRequest(RequestMethodInterface::METHOD_GET, ['alpha' => '@'], [], [], ['tree' => $this->tree]);
163        $response = $this->module->handle($request);
164        $html     = $response->getBody()->getContents();
165        preg_match_all('/%2Fname%2Findividual%2F(X\d+)%2F/', $html, $matches);
166        self::assertEqualsCanonicalizing([$i1->xref()], $matches[1]);
167
168        $request  = self::createRequest(RequestMethodInterface::METHOD_GET, ['alpha' => ','], [], [], ['tree' => $this->tree]);
169        $response = $this->module->handle($request);
170        $html     = $response->getBody()->getContents();
171        preg_match_all('/%2Fname%2Findividual%2F(X\d+)%2F/', $html, $matches);
172        self::assertEqualsCanonicalizing([$i2->xref()], $matches[1]);
173    }
174
175    /**
176     * @covers \Fisharebest\Webtrees\Module\IndividualListModule
177     */
178    public function testAllSurnamesExcludesUnknownAndMissing(): void
179    {
180        $i1 = $this->tree->createIndividual("0 @@ INDI\n1 NAME John /Black/");
181        $i2 = $this->tree->createIndividual("0 @@ INDI\n1 NAME Mary /White/");
182        $this->tree->createIndividual("0 @@ INDI\n1 NAME Peter //");
183        $this->tree->createIndividual("0 @@ INDI\n1 NAME Paul");
184
185        $request  = self::createRequest(RequestMethodInterface::METHOD_GET, ['show_all' => 'yes'], [], [], ['tree' => $this->tree]);
186        $response = $this->module->handle($request);
187        $html     = $response->getBody()->getContents();
188        preg_match_all('/individual-list&amp;surname=([A-Z]+)/', $html, $matches);
189        self::assertEqualsCanonicalizing(['BLACK', 'WHITE'], $matches[1]);
190
191        $request  = self::createRequest(RequestMethodInterface::METHOD_GET, ['show_all' => 'yes', 'show' => 'indi'], [], [], ['tree' => $this->tree]);
192        $response = $this->module->handle($request);
193        $html     = $response->getBody()->getContents();
194        preg_match_all('/%2Fname%2Findividual%2F(X\d+)%2F/', $html, $matches);
195        self::assertEqualsCanonicalizing([$i1->xref(), $i2->xref()], $matches[1]);
196    }
197
198    /**
199     * @covers \Fisharebest\Webtrees\Module\IndividualListModule
200     */
201    public function testSurnameInitial(): void
202    {
203        $i1 = $this->tree->createIndividual("0 @@ INDI\n1 NAME John /Black/");
204        $i2 = $this->tree->createIndividual("0 @@ INDI\n1 NAME Mary /Brown/");
205        $this->tree->createIndividual("0 @@ INDI\n1 NAME Peter /White/");
206        $this->tree->createIndividual("0 @@ INDI\n1 NAME Paul /Green/");
207
208        $request  = self::createRequest(RequestMethodInterface::METHOD_GET, ['alpha' => 'B'], [], [], ['tree' => $this->tree]);
209        $response = $this->module->handle($request);
210        $html     = $response->getBody()->getContents();
211        preg_match_all('/individual-list&amp;surname=([A-Z]+)/', $html, $matches);
212        self::assertEqualsCanonicalizing(['BLACK', 'BROWN'], $matches[1]);
213
214        $request  = self::createRequest(RequestMethodInterface::METHOD_GET, ['alpha' => 'B', 'show' => 'indi'], [], [], ['tree' => $this->tree]);
215        $response = $this->module->handle($request);
216        $html     = $response->getBody()->getContents();
217        preg_match_all('/%2Fname%2Findividual%2F(X\d+)%2F/', $html, $matches);
218        self::assertEqualsCanonicalizing([$i1->xref(), $i2->xref()], $matches[1]);
219    }
220}
221