Skip to content

Commit 7684a59

Browse files
Improved support for Codeception 5/PHP 8 (#53)
Co-authored-by: Dan Barrett <dan@theblahman.net>
1 parent 17a740e commit 7684a59

File tree

7 files changed

+121
-132
lines changed

7 files changed

+121
-132
lines changed

composer.json

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
{
22
"name": "codeception/lib-innerbrowser",
33
"description": "Parent library for all Codeception framework modules and PhpBrowser",
4-
"keywords": [ "codeception" ],
5-
"homepage": "https://codeception.com/",
6-
"type": "library",
74
"license": "MIT",
5+
"type": "library",
6+
"keywords": [
7+
"codeception"
8+
],
89
"authors": [
910
{
1011
"name": "Michael Bodnarchuk",
@@ -15,29 +16,31 @@
1516
"name": "Gintautas Miselis"
1617
}
1718
],
18-
"minimum-stability": "dev",
19+
"homepage": "https://codeception.com/",
1920
"require": {
2021
"php": "^8.0",
2122
"ext-dom": "*",
2223
"ext-json": "*",
2324
"ext-mbstring": "*",
24-
"codeception/codeception": "^5.0.0-alpha1",
25-
"symfony/browser-kit": "^4.4 | ^5.4 | ^6.0",
26-
"symfony/dom-crawler": "^4.4 | ^5.4 | ^6.0"
25+
"codeception/codeception": "dev-5.0-interfaces as 5.0.0",
26+
"symfony/browser-kit": "^4.4 || ^5.4 || ^6.0",
27+
"symfony/dom-crawler": "^4.4 || ^5.4 || ^6.0"
2728
},
2829
"require-dev": {
2930
"codeception/util-universalframework": "dev-master"
3031
},
3132
"conflict": {
3233
"codeception/codeception": "<5.0"
3334
},
35+
"minimum-stability": "dev",
3436
"autoload": {
3537
"classmap": [
3638
"src/"
3739
]
3840
},
3941
"config": {
40-
"classmap-authoritative": true
42+
"classmap-authoritative": true,
43+
"sort-packages": true
4144
},
4245
"scripts": {
4346
"test": "codecept run --coverage-xml"

src/Codeception/Lib/InnerBrowser.php

Lines changed: 36 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ public function _failed(TestInterface $test, $fail)
7777
if (!$this->client || !$this->client->getInternalResponse()) {
7878
return;
7979
}
80-
} catch (BadMethodCallException $exception) {
80+
} catch (BadMethodCallException) {
8181
//Symfony 5 throws exception if request() method threw an exception.
8282
//The "request()" method must be called before "Symfony\Component\BrowserKit\AbstractBrowser::getInternalResponse()"
8383
return;
@@ -93,7 +93,7 @@ public function _failed(TestInterface $test, $fail)
9393

9494
try {
9595
$internalResponse = $this->client->getInternalResponse();
96-
} catch (BadMethodCallException $exception) {
96+
} catch (BadMethodCallException) {
9797
$internalResponse = false;
9898
}
9999

@@ -124,7 +124,7 @@ public function _conflicts(): string
124124
return \Codeception\Lib\Interfaces\Web::class;
125125
}
126126

127-
public function _findElements($locator)
127+
public function _findElements(mixed $locator): iterable
128128
{
129129
return $this->match($locator);
130130
}
@@ -215,7 +215,7 @@ protected function clientRequest(
215215
if (preg_match('#^(//|https?://(?!localhost))#', $uri)) {
216216
$hostname = parse_url($uri, PHP_URL_HOST);
217217
if (!$this->isInternalDomain($hostname)) {
218-
throw new ExternalUrlException(get_class($this) . " can't open external URL: " . $uri);
218+
throw new ExternalUrlException($this::class . " can't open external URL: " . $uri);
219219
}
220220
}
221221

@@ -308,7 +308,7 @@ private function getRunningClient(): AbstractBrowser
308308
"Page not loaded. Use `\$I->amOnPage` (or hidden API methods `_request` and `_loadPage`) to open it"
309309
);
310310
}
311-
} catch (BadMethodCallException $e) {
311+
} catch (BadMethodCallException) {
312312
//Symfony 5
313313
throw new ModuleException(
314314
$this,
@@ -421,15 +421,15 @@ public function click($link, $context = null): void
421421

422422
try {
423423
$this->clickByLocator($link);
424-
} catch (MalformedLocatorException $exception) {
424+
} catch (MalformedLocatorException) {
425425
throw new ElementNotFound("name={$link}", "'{$link}' is invalid CSS and XPath selector and Link or Button");
426426
}
427427
}
428428

429429
/**
430430
* @param string|string[] $link
431431
*/
432-
protected function clickByLocator($link): ?bool
432+
protected function clickByLocator(string|array $link): ?bool
433433
{
434434
$nodes = $this->match($link);
435435
if ($nodes->count() === 0) {
@@ -633,7 +633,7 @@ public function dontSeeCurrentUrlMatches(string $uri): void
633633
$this->assertNotRegExp($uri, $this->_getCurrentUri());
634634
}
635635

636-
public function grabFromCurrentUrl($uri = null)
636+
public function grabFromCurrentUrl(string $uri = null): mixed
637637
{
638638
if (!$uri) {
639639
return $this->_getCurrentUri();
@@ -722,7 +722,6 @@ protected function proceedSeeInFormFields($formSelector, array $params, $assertN
722722
* @param SymfonyCrawler $form The form in which to search for fields.
723723
* @param string $name The field's name.
724724
* @param mixed $values
725-
* @return void
726725
*/
727726
protected function pushFormField(array &$fields, SymfonyCrawler $form, string $name, $values): void
728727
{
@@ -770,10 +769,8 @@ protected function proceedSeeInField(Crawler $fields, $value): array
770769

771770
/**
772771
* Get the values of a set of fields and also the texts of selected options.
773-
*
774-
* @return array|string
775772
*/
776-
protected function getValueAndTextFromField(Crawler $nodes)
773+
protected function getValueAndTextFromField(Crawler $nodes): array|string
777774
{
778775
if ($nodes->filter('textarea')->count() !== 0) {
779776
return (new TextareaFormField($nodes->filter('textarea')->getNode(0)))->getValue();
@@ -802,10 +799,8 @@ protected function getValueAndTextFromField(Crawler $nodes)
802799

803800
/**
804801
* Get the values of a set of input fields.
805-
*
806-
* @return array|string
807802
*/
808-
protected function getInputValue(SymfonyCrawler $input)
803+
protected function getInputValue(SymfonyCrawler $input): array|string
809804
{
810805
$inputType = $input->attr('type');
811806
if ($inputType === 'checkbox' || $inputType === 'radio') {
@@ -830,7 +825,7 @@ protected function getInputValue(SymfonyCrawler $input)
830825
*/
831826
protected function getSubmissionFormFieldName(string $name): string
832827
{
833-
if (substr($name, -2) === '[]') {
828+
if (str_ends_with($name, '[]')) {
834829
return substr($name, 0, -2);
835830
}
836831

@@ -857,7 +852,7 @@ protected function setCheckboxBoolValues(Crawler $form, array $params): array
857852
$chFoundByName = [];
858853
foreach ($checkboxes as $checkbox) {
859854
$fieldName = $this->getSubmissionFormFieldName($checkbox->getAttribute('name'));
860-
$pos = (isset($chFoundByName[$fieldName])) ? $chFoundByName[$fieldName] : 0;
855+
$pos = $chFoundByName[$fieldName] ?? 0;
861856
$skip = !isset($params[$fieldName])
862857
|| (!is_array($params[$fieldName]) && !is_bool($params[$fieldName]))
863858
|| (is_array($params[$fieldName]) &&
@@ -966,7 +961,7 @@ public function submitForm($selector, array $params, string $button = null): voi
966961
protected function getAbsoluteUrlFor(string $uri): string
967962
{
968963
$currentUrl = $this->getRunningClient()->getHistory()->current()->getUri();
969-
if (empty($uri) || strpos($uri, '#') === 0) {
964+
if (empty($uri) || str_starts_with($uri, '#')) {
970965
return $currentUrl;
971966
}
972967

@@ -1111,7 +1106,7 @@ protected function getFormValuesFor(SymfonyForm $form): array
11111106

11121107

11131108
$fieldName = $this->getSubmissionFormFieldName($field->getName());
1114-
if (substr($field->getName(), -2) === '[]') {
1109+
if (str_ends_with($field->getName(), '[]')) {
11151110
if (!isset($values[$fieldName])) {
11161111
$values[$fieldName] = [];
11171112
}
@@ -1146,6 +1141,7 @@ public function fillField($field, $value): void
11461141

11471142
protected function getFieldsByLabelOrCss($field): SymfonyCrawler
11481143
{
1144+
$input = null;
11491145
if (is_array($field)) {
11501146
$input = $this->strictMatch($field);
11511147
if (count($input) === 0) {
@@ -1233,10 +1229,9 @@ public function selectOption($select, $option): void
12331229
}
12341230

12351231
/**
1236-
* @param string|array $option
12371232
* @return mixed
12381233
*/
1239-
protected function matchOption(Crawler $field, $option)
1234+
protected function matchOption(Crawler $field, string|array $option)
12401235
{
12411236
if (isset($option['value'])) {
12421237
return $option['value'];
@@ -1280,7 +1275,7 @@ public function uncheckOption($option): void
12801275
/**
12811276
* @param string|string[] $option
12821277
*/
1283-
protected function proceedCheckOption($option): ChoiceFormField
1278+
protected function proceedCheckOption(string|array $option): ChoiceFormField
12841279
{
12851280
$form = $this->getFormFor($field = $this->getFieldByLabelOrCss($option));
12861281
$name = $field->attr('name');
@@ -1419,7 +1414,7 @@ protected function getResponseStatusCode()
14191414
/**
14201415
* @param string|string[] $selector
14211416
*/
1422-
protected function match($selector): SymfonyCrawler
1417+
protected function match(string|array $selector): SymfonyCrawler
14231418
{
14241419
if (is_array($selector)) {
14251420
return $this->strictMatch($selector);
@@ -1444,24 +1439,17 @@ protected function strictMatch(array $by): SymfonyCrawler
14441439
{
14451440
$type = key($by);
14461441
$locator = $by[$type];
1447-
switch ($type) {
1448-
case 'id':
1449-
return $this->filterByCSS(sprintf('#%s', $locator));
1450-
case 'name':
1451-
return $this->filterByXPath(sprintf('.//*[@name=%s]', SymfonyCrawler::xpathLiteral($locator)));
1452-
case 'css':
1453-
return $this->filterByCSS($locator);
1454-
case 'xpath':
1455-
return $this->filterByXPath($locator);
1456-
case 'link':
1457-
return $this->filterByXPath(sprintf('.//a[.=%s or contains(./@title, %s)]', SymfonyCrawler::xpathLiteral($locator), SymfonyCrawler::xpathLiteral($locator)));
1458-
case 'class':
1459-
return $this->filterByCSS(".{$locator}");
1460-
default:
1461-
throw new TestRuntimeException(
1462-
"Locator type '{$by}' is not defined. Use either: xpath, css, id, link, class, name"
1463-
);
1464-
}
1442+
return match ($type) {
1443+
'id' => $this->filterByCSS(sprintf('#%s', $locator)),
1444+
'name' => $this->filterByXPath(sprintf('.//*[@name=%s]', SymfonyCrawler::xpathLiteral($locator))),
1445+
'css' => $this->filterByCSS($locator),
1446+
'xpath' => $this->filterByXPath($locator),
1447+
'link' => $this->filterByXPath(sprintf('.//a[.=%s or contains(./@title, %s)]', SymfonyCrawler::xpathLiteral($locator), SymfonyCrawler::xpathLiteral($locator))),
1448+
'class' => $this->filterByCSS(".{$locator}"),
1449+
default => throw new TestRuntimeException(
1450+
"Locator type '{$by}' is not defined. Use either: xpath, css, id, link, class, name"
1451+
),
1452+
};
14651453
}
14661454

14671455
protected function filterByAttributes(Crawler $nodes, array $attributes)
@@ -1475,7 +1463,7 @@ protected function filterByAttributes(Crawler $nodes, array $attributes)
14751463
return $nodes;
14761464
}
14771465

1478-
public function grabTextFrom($cssOrXPathOrRegex)
1466+
public function grabTextFrom($cssOrXPathOrRegex): mixed
14791467
{
14801468
if (is_string($cssOrXPathOrRegex) && @preg_match($cssOrXPathOrRegex, $this->client->getInternalResponse()->getContent(), $matches)) {
14811469
return $matches[1];
@@ -1489,7 +1477,7 @@ public function grabTextFrom($cssOrXPathOrRegex)
14891477
throw new ElementNotFound($cssOrXPathOrRegex, 'Element that matches CSS or XPath or Regex');
14901478
}
14911479

1492-
public function grabAttributeFrom($cssOrXpath, $attribute)
1480+
public function grabAttributeFrom($cssOrXpath, string $attribute): mixed
14931481
{
14941482
$nodes = $this->match($cssOrXpath);
14951483
if ($nodes->count() === 0) {
@@ -1511,7 +1499,7 @@ public function grabMultiple($cssOrXpath, string $attribute = null): array
15111499
return $result;
15121500
}
15131501

1514-
public function grabValueFrom($field)
1502+
public function grabValueFrom($field): mixed
15151503
{
15161504
$nodes = $this->match($field);
15171505
if ($nodes->count() === 0) {
@@ -1566,7 +1554,7 @@ public function setCookie($name, $val, $params = [])
15661554
$this->debugCookieJar();
15671555
}
15681556

1569-
public function grabCookie($cookie, $params = [])
1557+
public function grabCookie(string $cookie, array $params = []): mixed
15701558
{
15711559
$params = array_merge($this->defaultCookieParameters, $params);
15721560
$this->debugCookieJar();
@@ -1869,11 +1857,10 @@ protected function assertPageSourceNotContains(string $needle, string $message =
18691857

18701858
/**
18711859
* @param array|object $form
1872-
* @return FormField|array
18731860
*/
1874-
protected function matchFormField(string $name, $form, FormField $dynamicField)
1861+
protected function matchFormField(string $name, $form, FormField $dynamicField): FormField|array
18751862
{
1876-
if (substr($name, -2) !== '[]') {
1863+
if (!str_ends_with($name, '[]')) {
18771864
return $form[$name];
18781865
}
18791866

@@ -1986,6 +1973,7 @@ public function switchToIframe(string $name): void
19861973
*/
19871974
public function moveBack(int $numberOfSteps = 1): void
19881975
{
1976+
$request = null;
19891977
if (!is_int($numberOfSteps) || $numberOfSteps < 1) {
19901978
throw new InvalidArgumentException('numberOfSteps must be positive integer');
19911979
}

0 commit comments

Comments
 (0)