1<?php
2
3namespace Sabre\VObject\Component;
4
5use Sabre\VObject;
6
7class VCardTest extends \PHPUnit_Framework_TestCase {
8
9    /**
10     * @dataProvider validateData
11     */
12    function testValidate($input, $expectedWarnings, $expectedRepairedOutput) {
13
14        $vcard = VObject\Reader::read($input);
15
16        $warnings = $vcard->validate();
17
18        $warnMsg = [];
19        foreach ($warnings as $warning) {
20            $warnMsg[] = $warning['message'];
21        }
22
23        $this->assertEquals($expectedWarnings, $warnMsg);
24
25        $vcard->validate(VObject\Component::REPAIR);
26
27        $this->assertEquals(
28            $expectedRepairedOutput,
29            $vcard->serialize()
30        );
31
32    }
33
34    function validateData() {
35
36        $tests = [];
37
38        // Correct
39        $tests[] = [
40            "BEGIN:VCARD\r\nVERSION:4.0\r\nFN:John Doe\r\nUID:foo\r\nEND:VCARD\r\n",
41            [],
42            "BEGIN:VCARD\r\nVERSION:4.0\r\nFN:John Doe\r\nUID:foo\r\nEND:VCARD\r\n",
43        ];
44
45        // No VERSION
46        $tests[] = [
47            "BEGIN:VCARD\r\nFN:John Doe\r\nUID:foo\r\nEND:VCARD\r\n",
48            [
49                'VERSION MUST appear exactly once in a VCARD component',
50            ],
51            "BEGIN:VCARD\r\nVERSION:4.0\r\nFN:John Doe\r\nUID:foo\r\nEND:VCARD\r\n",
52        ];
53
54        // Unknown version
55        $tests[] = [
56            "BEGIN:VCARD\r\nVERSION:2.2\r\nFN:John Doe\r\nUID:foo\r\nEND:VCARD\r\n",
57            [
58                'Only vcard version 4.0 (RFC6350), version 3.0 (RFC2426) or version 2.1 (icm-vcard-2.1) are supported.',
59            ],
60            "BEGIN:VCARD\r\nVERSION:2.1\r\nFN:John Doe\r\nUID:foo\r\nEND:VCARD\r\n",
61        ];
62
63        // No FN
64        $tests[] = [
65            "BEGIN:VCARD\r\nVERSION:4.0\r\nUID:foo\r\nEND:VCARD\r\n",
66            [
67                'The FN property must appear in the VCARD component exactly 1 time',
68            ],
69            "BEGIN:VCARD\r\nVERSION:4.0\r\nUID:foo\r\nEND:VCARD\r\n",
70        ];
71        // No FN, N fallback
72        $tests[] = [
73            "BEGIN:VCARD\r\nVERSION:4.0\r\nUID:foo\r\nN:Doe;John;;;;;\r\nEND:VCARD\r\n",
74            [
75                'The FN property must appear in the VCARD component exactly 1 time',
76            ],
77            "BEGIN:VCARD\r\nVERSION:4.0\r\nUID:foo\r\nN:Doe;John;;;;;\r\nFN:John Doe\r\nEND:VCARD\r\n",
78        ];
79        // No FN, N fallback, no first name
80        $tests[] = [
81            "BEGIN:VCARD\r\nVERSION:4.0\r\nUID:foo\r\nN:Doe;;;;;;\r\nEND:VCARD\r\n",
82            [
83                'The FN property must appear in the VCARD component exactly 1 time',
84            ],
85            "BEGIN:VCARD\r\nVERSION:4.0\r\nUID:foo\r\nN:Doe;;;;;;\r\nFN:Doe\r\nEND:VCARD\r\n",
86        ];
87
88        // No FN, ORG fallback
89        $tests[] = [
90            "BEGIN:VCARD\r\nVERSION:4.0\r\nUID:foo\r\nORG:Acme Co.\r\nEND:VCARD\r\n",
91            [
92                'The FN property must appear in the VCARD component exactly 1 time',
93            ],
94            "BEGIN:VCARD\r\nVERSION:4.0\r\nUID:foo\r\nORG:Acme Co.\r\nFN:Acme Co.\r\nEND:VCARD\r\n",
95        ];
96        return $tests;
97
98    }
99
100    function testGetDocumentType() {
101
102        $vcard = new VCard([], false);
103        $vcard->VERSION = '2.1';
104        $this->assertEquals(VCard::VCARD21, $vcard->getDocumentType());
105
106        $vcard = new VCard([], false);
107        $vcard->VERSION = '3.0';
108        $this->assertEquals(VCard::VCARD30, $vcard->getDocumentType());
109
110        $vcard = new VCard([], false);
111        $vcard->VERSION = '4.0';
112        $this->assertEquals(VCard::VCARD40, $vcard->getDocumentType());
113
114        $vcard = new VCard([], false);
115        $this->assertEquals(VCard::UNKNOWN, $vcard->getDocumentType());
116    }
117
118    function testGetByType() {
119        $vcard = <<<VCF
120BEGIN:VCARD
121VERSION:3.0
122EMAIL;TYPE=home:1@example.org
123EMAIL;TYPE=work:2@example.org
124END:VCARD
125VCF;
126
127        $vcard = VObject\Reader::read($vcard);
128        $this->assertEquals('1@example.org', $vcard->getByType('EMAIL', 'home')->getValue());
129        $this->assertEquals('2@example.org', $vcard->getByType('EMAIL', 'work')->getValue());
130        $this->assertNull($vcard->getByType('EMAIL', 'non-existant'));
131        $this->assertNull($vcard->getByType('ADR', 'non-existant'));
132    }
133
134    function testPreferredNoPref() {
135
136        $vcard = <<<VCF
137BEGIN:VCARD
138VERSION:3.0
139EMAIL:1@example.org
140EMAIL:2@example.org
141END:VCARD
142VCF;
143
144        $vcard = VObject\Reader::read($vcard);
145        $this->assertEquals('1@example.org', $vcard->preferred('EMAIL')->getValue());
146
147    }
148
149    function testPreferredWithPref() {
150
151        $vcard = <<<VCF
152BEGIN:VCARD
153VERSION:3.0
154EMAIL:1@example.org
155EMAIL;TYPE=PREF:2@example.org
156END:VCARD
157VCF;
158
159        $vcard = VObject\Reader::read($vcard);
160        $this->assertEquals('2@example.org', $vcard->preferred('EMAIL')->getValue());
161
162    }
163
164    function testPreferredWith40Pref() {
165
166        $vcard = <<<VCF
167BEGIN:VCARD
168VERSION:4.0
169EMAIL:1@example.org
170EMAIL;PREF=3:2@example.org
171EMAIL;PREF=2:3@example.org
172END:VCARD
173VCF;
174
175        $vcard = VObject\Reader::read($vcard);
176        $this->assertEquals('3@example.org', $vcard->preferred('EMAIL')->getValue());
177
178    }
179
180    function testPreferredNotFound() {
181
182        $vcard = <<<VCF
183BEGIN:VCARD
184VERSION:4.0
185END:VCARD
186VCF;
187
188        $vcard = VObject\Reader::read($vcard);
189        $this->assertNull($vcard->preferred('EMAIL'));
190
191    }
192
193    function testNoUIDCardDAV() {
194
195        $vcard = <<<VCF
196BEGIN:VCARD
197VERSION:4.0
198FN:John Doe
199END:VCARD
200VCF;
201        $this->assertValidate(
202            $vcard,
203            VCARD::PROFILE_CARDDAV,
204            3,
205            'vCards on CardDAV servers MUST have a UID property.'
206        );
207
208    }
209
210    function testNoUIDNoCardDAV() {
211
212        $vcard = <<<VCF
213BEGIN:VCARD
214VERSION:4.0
215FN:John Doe
216END:VCARD
217VCF;
218        $this->assertValidate(
219            $vcard,
220            0,
221            2,
222            'Adding a UID to a vCard property is recommended.'
223        );
224
225    }
226    function testNoUIDNoCardDAVRepair() {
227
228        $vcard = <<<VCF
229BEGIN:VCARD
230VERSION:4.0
231FN:John Doe
232END:VCARD
233VCF;
234        $this->assertValidate(
235            $vcard,
236            VCARD::REPAIR,
237            1,
238            'Adding a UID to a vCard property is recommended.'
239        );
240
241    }
242
243    function testVCard21CardDAV() {
244
245        $vcard = <<<VCF
246BEGIN:VCARD
247VERSION:2.1
248FN:John Doe
249UID:foo
250END:VCARD
251VCF;
252        $this->assertValidate(
253            $vcard,
254            VCARD::PROFILE_CARDDAV,
255            3,
256            'CardDAV servers are not allowed to accept vCard 2.1.'
257        );
258
259    }
260
261    function testVCard21NoCardDAV() {
262
263        $vcard = <<<VCF
264BEGIN:VCARD
265VERSION:2.1
266FN:John Doe
267UID:foo
268END:VCARD
269VCF;
270        $this->assertValidate(
271            $vcard,
272            0,
273            0
274        );
275
276    }
277
278    function assertValidate($vcf, $options, $expectedLevel, $expectedMessage = null) {
279
280        $vcal = VObject\Reader::read($vcf);
281        $result = $vcal->validate($options);
282
283        $this->assertValidateResult($result, $expectedLevel, $expectedMessage);
284
285    }
286
287    function assertValidateResult($input, $expectedLevel, $expectedMessage = null) {
288
289        $messages = [];
290        foreach ($input as $warning) {
291            $messages[] = $warning['message'];
292        }
293
294        if ($expectedLevel === 0) {
295            $this->assertEquals(0, count($input), 'No validation messages were expected. We got: ' . implode(', ', $messages));
296        } else {
297            $this->assertEquals(1, count($input), 'We expected exactly 1 validation message, We got: ' . implode(', ', $messages));
298
299            $this->assertEquals($expectedMessage, $input[0]['message']);
300            $this->assertEquals($expectedLevel, $input[0]['level']);
301        }
302
303    }
304}
305