Skip to content

Commit 7d9317f

Browse files
authored
Add Symfony-inherited assertions (Browser, DomCrawler, Mailer, Mime) (#240)
Bring assertion/getter methods that mirror Symfony's own test API into the matching module traits (assert*/get* family, per the naming convention): - BrowserAssertionsTrait: assertBrowserHistoryIsOnFirstPage / assertBrowserHistoryIsNotOnFirstPage / assertBrowserHistoryIsOnLastPage / assertBrowserHistoryIsNotOnLastPage (wrap symfony/browser-kit constraints) - DomCrawlerAssertionsTrait: assertSelectorCount, assertAnySelectorTextContains, assertAnySelectorTextSame, assertAnySelectorTextNotContains - MailerAssertionsTrait: getMailerEvents, getMailerMessages, getMailerMessage - MimeAssertionsTrait: assertEmailAddressNotContains, assertEmailSubjectContains, assertEmailSubjectNotContains
1 parent 4901a7d commit 7d9317f

8 files changed

Lines changed: 429 additions & 2 deletions

‎src/Codeception/Module/Symfony/BrowserAssertionsTrait.php‎

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
use PHPUnit\Framework\Constraint\LogicalNot;
1111
use Symfony\Component\BrowserKit\Test\Constraint\BrowserCookieValueSame;
1212
use Symfony\Component\BrowserKit\Test\Constraint\BrowserHasCookie;
13+
use Symfony\Component\BrowserKit\Test\Constraint\BrowserHistoryIsOnFirstPage;
14+
use Symfony\Component\BrowserKit\Test\Constraint\BrowserHistoryIsOnLastPage;
1315
use Symfony\Component\HttpFoundation\Test\Constraint\RequestAttributeValueSame;
1416
use Symfony\Component\HttpFoundation\Test\Constraint\ResponseCookieValueSame;
1517
use Symfony\Component\HttpFoundation\Test\Constraint\ResponseFormatSame;
@@ -71,6 +73,78 @@ public function assertBrowserNotHasCookie(string $name, string $path = '/', ?str
7173
$this->assertThatForClient(new LogicalNot(new BrowserHasCookie($name, $path, $domain)), $message);
7274
}
7375

76+
/**
77+
* Asserts that the browser history is currently on the first page.
78+
*
79+
* ```php
80+
* <?php
81+
* $I->assertBrowserHistoryIsOnFirstPage();
82+
* ```
83+
*/
84+
public function assertBrowserHistoryIsOnFirstPage(string $message = ''): void
85+
{
86+
$this->assertTrue(
87+
class_exists(BrowserHistoryIsOnFirstPage::class),
88+
'The assertBrowserHistoryIsOnFirstPage method requires symfony/browser-kit >= 7.4.'
89+
);
90+
91+
$this->assertThatForClient(new BrowserHistoryIsOnFirstPage(), $message);
92+
}
93+
94+
/**
95+
* Asserts that the browser history is not currently on the first page.
96+
*
97+
* ```php
98+
* <?php
99+
* $I->assertBrowserHistoryIsNotOnFirstPage();
100+
* ```
101+
*/
102+
public function assertBrowserHistoryIsNotOnFirstPage(string $message = ''): void
103+
{
104+
$this->assertTrue(
105+
class_exists(BrowserHistoryIsOnFirstPage::class),
106+
'The assertBrowserHistoryIsNotOnFirstPage method requires symfony/browser-kit >= 7.4.'
107+
);
108+
109+
$this->assertThatForClient(new LogicalNot(new BrowserHistoryIsOnFirstPage()), $message);
110+
}
111+
112+
/**
113+
* Asserts that the browser history is currently on the last page.
114+
*
115+
* ```php
116+
* <?php
117+
* $I->assertBrowserHistoryIsOnLastPage();
118+
* ```
119+
*/
120+
public function assertBrowserHistoryIsOnLastPage(string $message = ''): void
121+
{
122+
$this->assertTrue(
123+
class_exists(BrowserHistoryIsOnLastPage::class),
124+
'The assertBrowserHistoryIsOnLastPage method requires symfony/browser-kit >= 7.4.'
125+
);
126+
127+
$this->assertThatForClient(new BrowserHistoryIsOnLastPage(), $message);
128+
}
129+
130+
/**
131+
* Asserts that the browser history is not currently on the last page.
132+
*
133+
* ```php
134+
* <?php
135+
* $I->assertBrowserHistoryIsNotOnLastPage();
136+
* ```
137+
*/
138+
public function assertBrowserHistoryIsNotOnLastPage(string $message = ''): void
139+
{
140+
$this->assertTrue(
141+
class_exists(BrowserHistoryIsOnLastPage::class),
142+
'The assertBrowserHistoryIsNotOnLastPage method requires symfony/browser-kit >= 7.4.'
143+
);
144+
145+
$this->assertThatForClient(new LogicalNot(new BrowserHistoryIsOnLastPage()), $message);
146+
}
147+
74148
/**
75149
* Asserts that the specified request attribute matches the expected value.
76150
*

‎src/Codeception/Module/Symfony/DomCrawlerAssertionsTrait.php‎

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,17 @@
55
namespace Codeception\Module\Symfony;
66

77
use PHPUnit\Framework\Constraint\Constraint;
8+
use PHPUnit\Framework\Constraint\LogicalAnd;
89
use PHPUnit\Framework\Constraint\LogicalNot;
10+
use Symfony\Component\DomCrawler\Test\Constraint\CrawlerAnySelectorTextContains;
11+
use Symfony\Component\DomCrawler\Test\Constraint\CrawlerAnySelectorTextSame;
912
use Symfony\Component\DomCrawler\Test\Constraint\CrawlerSelectorAttributeValueSame;
13+
use Symfony\Component\DomCrawler\Test\Constraint\CrawlerSelectorCount;
1014
use Symfony\Component\DomCrawler\Test\Constraint\CrawlerSelectorExists;
1115
use Symfony\Component\DomCrawler\Test\Constraint\CrawlerSelectorTextContains;
1216
use Symfony\Component\DomCrawler\Test\Constraint\CrawlerSelectorTextSame;
1317

18+
use function class_exists;
1419
use function sprintf;
1520

1621
trait DomCrawlerAssertionsTrait
@@ -119,6 +124,20 @@ public function assertSelectorNotExists(string $selector, string $message = ''):
119124
$this->assertThatCrawler(new LogicalNot(new CrawlerSelectorExists($selector)), $message);
120125
}
121126

127+
/**
128+
* Asserts that the given selector matches the expected number of elements.
129+
*
130+
* ```php
131+
* <?php
132+
* $I->assertSelectorCount(3, '.item');
133+
* ```
134+
*/
135+
public function assertSelectorCount(int $expectedCount, string $selector, string $message = ''): void
136+
{
137+
$this->assertDomCrawlerConstraintAvailable(CrawlerSelectorCount::class, __FUNCTION__);
138+
$this->assertThatCrawler(new CrawlerSelectorCount($expectedCount, $selector), $message);
139+
}
140+
122141
/**
123142
* Asserts that the first element matching the given selector contains the expected text.
124143
*
@@ -132,6 +151,48 @@ public function assertSelectorTextContains(string $selector, string $text, strin
132151
$this->assertThatCrawler(new CrawlerSelectorTextContains($selector, $text), $message);
133152
}
134153

154+
/**
155+
* Asserts that at least one element matching the given selector contains the expected text.
156+
*
157+
* ```php
158+
* <?php
159+
* $I->assertAnySelectorTextContains('.item', 'Available');
160+
* ```
161+
*/
162+
public function assertAnySelectorTextContains(string $selector, string $text, string $message = ''): void
163+
{
164+
$this->assertDomCrawlerConstraintAvailable(CrawlerAnySelectorTextContains::class, __FUNCTION__);
165+
166+
$this->assertThatCrawler(
167+
LogicalAnd::fromConstraints(
168+
new CrawlerSelectorExists($selector),
169+
new CrawlerAnySelectorTextContains($selector, $text)
170+
),
171+
$message
172+
);
173+
}
174+
175+
/**
176+
* Asserts that at least one element matching the given selector has exactly the expected text.
177+
*
178+
* ```php
179+
* <?php
180+
* $I->assertAnySelectorTextSame('.item', 'In stock');
181+
* ```
182+
*/
183+
public function assertAnySelectorTextSame(string $selector, string $text, string $message = ''): void
184+
{
185+
$this->assertDomCrawlerConstraintAvailable(CrawlerAnySelectorTextSame::class, __FUNCTION__);
186+
187+
$this->assertThatCrawler(
188+
LogicalAnd::fromConstraints(
189+
new CrawlerSelectorExists($selector),
190+
new CrawlerAnySelectorTextSame($selector, $text)
191+
),
192+
$message
193+
);
194+
}
195+
135196
/**
136197
* Asserts that the first element matching the given selector does not contain the expected text.
137198
*
@@ -145,6 +206,27 @@ public function assertSelectorTextNotContains(string $selector, string $text, st
145206
$this->assertThatCrawler(new LogicalNot(new CrawlerSelectorTextContains($selector, $text)), $message);
146207
}
147208

209+
/**
210+
* Asserts that no element matching the given selector contains the expected text.
211+
*
212+
* ```php
213+
* <?php
214+
* $I->assertAnySelectorTextNotContains('.item', 'Error');
215+
* ```
216+
*/
217+
public function assertAnySelectorTextNotContains(string $selector, string $text, string $message = ''): void
218+
{
219+
$this->assertDomCrawlerConstraintAvailable(CrawlerAnySelectorTextContains::class, __FUNCTION__);
220+
221+
$this->assertThatCrawler(
222+
LogicalAnd::fromConstraints(
223+
new CrawlerSelectorExists($selector),
224+
new LogicalNot(new CrawlerAnySelectorTextContains($selector, $text))
225+
),
226+
$message
227+
);
228+
}
229+
148230
/**
149231
* Asserts that the text of the first element matching the given selector equals the expected text.
150232
*
@@ -184,4 +266,16 @@ private function assertInputValue(string $fieldName, string $expectedValue, bool
184266
}
185267
$this->assertThatCrawler($constraint, $context);
186268
}
269+
270+
private function assertDomCrawlerConstraintAvailable(string $constraintClass, string $function): void
271+
{
272+
$this->assertTrue(
273+
class_exists($constraintClass),
274+
sprintf(
275+
"The '%s' assertion is not available with your installed symfony/dom-crawler version. Missing constraint: %s",
276+
$function,
277+
$constraintClass
278+
)
279+
);
280+
}
187281
}

‎src/Codeception/Module/Symfony/MailerAssertionsTrait.php‎

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,21 @@ public function seeEmailIsSent(int $expectedCount = 1): void
145145
$this->assertThat($this->getMessageMailerEvents(), new MailerConstraint\EmailCount($expectedCount));
146146
}
147147

148+
/**
149+
* Returns all mailer events for the given transport.
150+
*
151+
* ```php
152+
* <?php
153+
* $events = $I->getMailerEvents();
154+
* ```
155+
*
156+
* @return MessageEvent[]
157+
*/
158+
public function getMailerEvents(?string $transport = null): array
159+
{
160+
return $this->getMessageMailerEvents()->getEvents($transport);
161+
}
162+
148163
/**
149164
* Returns the mailer event at the specified index.
150165
*
@@ -155,7 +170,35 @@ public function seeEmailIsSent(int $expectedCount = 1): void
155170
*/
156171
public function getMailerEvent(int $index = 0, ?string $transport = null): ?MessageEvent
157172
{
158-
return $this->getMessageMailerEvents()->getEvents($transport)[$index] ?? null;
173+
return $this->getMailerEvents($transport)[$index] ?? null;
174+
}
175+
176+
/**
177+
* Returns all sent mailer messages for the given transport.
178+
*
179+
* ```php
180+
* <?php
181+
* $messages = $I->getMailerMessages();
182+
* ```
183+
*
184+
* @return RawMessage[]
185+
*/
186+
public function getMailerMessages(?string $transport = null): array
187+
{
188+
return $this->getMessageMailerEvents()->getMessages($transport);
189+
}
190+
191+
/**
192+
* Returns the mailer message at the specified index.
193+
*
194+
* ```php
195+
* <?php
196+
* $message = $I->getMailerMessage();
197+
* ```
198+
*/
199+
public function getMailerMessage(int $index = 0, ?string $transport = null): ?RawMessage
200+
{
201+
return $this->getMailerMessages($transport)[$index] ?? null;
159202
}
160203

161204
protected function grabLastSentRawMessage(): ?RawMessage

‎src/Codeception/Module/Symfony/MimeAssertionsTrait.php‎

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
use Symfony\Component\Mime\RawMessage;
1212
use Symfony\Component\Mime\Test\Constraint as MimeConstraint;
1313

14+
use function class_exists;
1415
use function sprintf;
1516

1617
trait MimeAssertionsTrait
@@ -30,6 +31,20 @@ public function assertEmailAddressContains(string $headerName, string $expectedV
3031
$this->assertThat($this->getMessageOrFail($email, __FUNCTION__), new MimeConstraint\EmailAddressContains($headerName, $expectedValue));
3132
}
3233

34+
/**
35+
* Verify that an email does not contain the given address in the specified header.
36+
* If no Message is specified, the last sent message is used instead.
37+
*
38+
* ```php
39+
* <?php
40+
* $I->assertEmailAddressNotContains('To', 'john_doe@example.com');
41+
* ```
42+
*/
43+
public function assertEmailAddressNotContains(string $headerName, string $expectedValue, ?Message $email = null): void
44+
{
45+
$this->assertThat($this->getMessageOrFail($email, __FUNCTION__), new LogicalNot(new MimeConstraint\EmailAddressContains($headerName, $expectedValue)));
46+
}
47+
3348
/**
3449
* Verify that an email has the specified number `$count` of attachments.
3550
* If no Email is specified, the last sent email is used instead.
@@ -158,6 +173,36 @@ public function assertEmailTextBodyNotContains(string $text, ?Email $email = nul
158173
$this->assertThat($this->getMessageOrFail($email, __FUNCTION__), new LogicalNot(new MimeConstraint\EmailTextBodyContains($text)));
159174
}
160175

176+
/**
177+
* Verify that an email subject contains `$expectedValue`.
178+
* If no Email is specified, the last sent email is used instead.
179+
*
180+
* ```php
181+
* <?php
182+
* $I->assertEmailSubjectContains('Account created successfully');
183+
* ```
184+
*/
185+
public function assertEmailSubjectContains(string $expectedValue, ?Email $email = null): void
186+
{
187+
$this->assertMimeConstraintAvailable(MimeConstraint\EmailSubjectContains::class, __FUNCTION__);
188+
$this->assertThat($this->getMessageOrFail($email, __FUNCTION__), new MimeConstraint\EmailSubjectContains($expectedValue));
189+
}
190+
191+
/**
192+
* Verify that an email subject does not contain `$expectedValue`.
193+
* If no Email is specified, the last sent email is used instead.
194+
*
195+
* ```php
196+
* <?php
197+
* $I->assertEmailSubjectNotContains('Password reset');
198+
* ```
199+
*/
200+
public function assertEmailSubjectNotContains(string $expectedValue, ?Email $email = null): void
201+
{
202+
$this->assertMimeConstraintAvailable(MimeConstraint\EmailSubjectContains::class, __FUNCTION__);
203+
$this->assertThat($this->getMessageOrFail($email, __FUNCTION__), new LogicalNot(new MimeConstraint\EmailSubjectContains($expectedValue)));
204+
}
205+
161206
/**
162207
* Resolves a Message for assertion or fails the test.
163208
*
@@ -174,4 +219,16 @@ private function getMessageOrFail(?Message $message, string $caller): Message
174219

175220
return $message;
176221
}
222+
223+
private function assertMimeConstraintAvailable(string $constraintClass, string $function): void
224+
{
225+
$this->assertTrue(
226+
class_exists($constraintClass),
227+
sprintf(
228+
"The '%s' assertion is not available with your installed symfony/mime version. Missing constraint: %s",
229+
$function,
230+
$constraintClass
231+
)
232+
);
233+
}
177234
}

0 commit comments

Comments
 (0)