first commit

This commit is contained in:
2026-01-25 18:18:09 +08:00
commit 509312e604
8136 changed files with 2349298 additions and 0 deletions

View File

@ -0,0 +1,36 @@
name: CI
on: [push, pull_request]
jobs:
tests:
runs-on: ubuntu-latest
env:
COMPOSER_ROOT_VERSION: 4.99.99
strategy:
matrix:
php: [8.1, 8.2, 8.3, 8.4, 8.5]
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
# this ini directive seems to be off by default in PHP 8.5
# see https://github.com/php/php-src/issues/20279
# enable it because codeception relies on it.
ini-values: register_argc_argv=1
- name: Validate composer.json and composer.lock
run: composer validate
- name: Install dependencies
run: composer install --prefer-dist --no-progress --no-interaction --no-suggest
- name: Run test suite
run: php vendor/bin/codecept run

View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2011 Michael Bodnarchuk and contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@ -0,0 +1,47 @@
{
"name": "codeception/lib-innerbrowser",
"description": "Parent library for all Codeception framework modules and PhpBrowser",
"license": "MIT",
"type": "library",
"keywords": [
"codeception"
],
"authors": [
{
"name": "Michael Bodnarchuk",
"email": "davert@mail.ua",
"homepage": "https://codegyre.com"
},
{
"name": "Gintautas Miselis"
}
],
"homepage": "https://codeception.com/",
"require": {
"php": "^8.1",
"ext-dom": "*",
"ext-json": "*",
"ext-mbstring": "*",
"codeception/codeception": "^5.0.8",
"codeception/lib-web": "^1.0.1 || ^2",
"phpunit/phpunit": "^10.0 || ^11.0 || ^12.0",
"symfony/browser-kit": "^4.4.24 || ^5.4 || ^6.0 || ^7.0 || ^8.0",
"symfony/dom-crawler": "^4.4.30 || ^5.4 || ^6.0 || ^7.0 || ^8.0"
},
"require-dev": {
"codeception/util-universalframework": "^1.0 || ^2.0"
},
"minimum-stability": "RC",
"autoload": {
"classmap": [
"src/"
]
},
"config": {
"classmap-authoritative": true,
"sort-packages": true
},
"scripts": {
"test": "codecept run --coverage-xml"
}
}

View File

@ -0,0 +1,91 @@
<?php
declare(strict_types=1);
namespace Codeception\Constraint;
use Codeception\Exception\ElementNotFound;
use DOMElement;
use PHPUnit\Framework\ExpectationFailedException;
use SebastianBergmann\Comparator\ComparisonFailure;
use Symfony\Component\DomCrawler\Crawler as SymfonyDomCrawler;
use function rtrim;
use function sprintf;
use function strpos;
class Crawler extends Page
{
/**
* @param SymfonyDomCrawler $nodes
* @return bool
*/
protected function matches($nodes): bool
{
if (!$nodes->count()) {
return false;
}
if ($this->string === '') {
return true;
}
foreach ($nodes as $node) {
if (parent::matches($node->nodeValue)) {
return true;
}
}
return false;
}
/**
* @param SymfonyDomCrawler $nodes
* @param string $selector
* @param ComparisonFailure|null $comparisonFailure
*/
protected function fail($nodes, $selector, ?ComparisonFailure $comparisonFailure = null): never
{
if (!$nodes->count()) {
throw new ElementNotFound($selector, 'Element located either by name, CSS or XPath');
}
$output = "Failed asserting that any element by '{$selector}' ";
$output .= $this->uriMessage('on page');
if ($nodes->count() < 10) {
$output .= $this->nodesList($nodes);
} else {
$output = sprintf('%s [total %d elements]', rtrim($output, ' '), $nodes->count());
}
$output .= "\ncontains text '{$this->string}'";
throw new ExpectationFailedException(
$output,
$comparisonFailure
);
}
/**
* @param DOMElement[] $other
* @return string
*/
protected function failureDescription($other): string
{
$description = '';
foreach ($other as $o) {
$description .= parent::failureDescription($o->textContent);
}
return $description;
}
protected function nodesList(SymfonyDomCrawler $domCrawler, ?string $contains = null): string
{
$output = '';
foreach ($domCrawler as $node) {
if ($contains && strpos($node->nodeValue, $contains) === false) {
continue;
}
$output .= "\n+ " . $node->C14N();
}
return $output;
}
}

View File

@ -0,0 +1,54 @@
<?php
declare(strict_types=1);
namespace Codeception\Constraint;
use PHPUnit\Framework\ExpectationFailedException;
use SebastianBergmann\Comparator\ComparisonFailure;
use Symfony\Component\DomCrawler\Crawler as SymfonyCrawler;
class CrawlerNot extends Crawler
{
/**
* @param SymfonyCrawler $nodes
* @return bool
*/
protected function matches($nodes): bool
{
return !parent::matches($nodes);
}
/**
* @param SymfonyCrawler $nodes
* @param string $selector
* @param ComparisonFailure|null $comparisonFailure
*/
protected function fail($nodes, $selector, ?ComparisonFailure $comparisonFailure = null): never
{
if (!$this->string) {
throw new ExpectationFailedException(
"Element '{$selector}' was found",
$comparisonFailure
);
}
$output = "There was '{$selector}' element ";
$output .= $this->uriMessage('on page');
$output .= $this->nodesList($nodes, $this->string);
$output .= "\ncontaining '{$this->string}'";
throw new ExpectationFailedException(
$output,
$comparisonFailure
);
}
public function toString(): string
{
if ($this->string) {
return 'that contains text "' . $this->string . '"';
}
return '';
}
}

View File

@ -0,0 +1,11 @@
<?php
declare(strict_types=1);
namespace Codeception\Exception;
use Exception;
class ExternalUrlException extends Exception
{
}

View File

@ -0,0 +1,29 @@
<?php
declare(strict_types=1);
namespace Codeception\Lib;
/**
* Abstract module for PHP frameworks connected via Symfony BrowserKit components
* Each framework is connected with it's own connector defined in \Codeception\Lib\Connector
* Each module for framework should extend this class.
*/
abstract class Framework extends InnerBrowser
{
/**
* Returns a list of recognized domain names
*/
protected function getInternalDomains(): array
{
return [];
}
public function _beforeSuite($settings = [])
{
/**
* reset internal domains before suite, because each suite can have a different configuration
*/
$this->internalDomains = null;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,357 @@
<?php
declare(strict_types=1);
namespace Codeception\Util;
/**
* Class containing constants of HTTP Status Codes
* and method to print HTTP code with its description.
*
* Usage:
*
* ```php
* <?php
* use Codeception\Util\HttpCode;
*
* // using REST, PhpBrowser, or any Framework module
* $I->seeResponseCodeIs(HttpCode::OK);
* $I->dontSeeResponseCodeIs(HttpCode::NOT_FOUND);
* ```
*/
class HttpCode
{
// const CONTINUE = 100;
/**
* @var int
*/
public const SWITCHING_PROTOCOLS = 101;
/**
* @var int
*/
public const PROCESSING = 102; // RFC2518
/**
* @var int
*/
public const EARLY_HINTS = 103; // RFC8297
/**
* @var int
*/
public const OK = 200;
/**
* @var int
*/
public const CREATED = 201;
/**
* @var int
*/
public const ACCEPTED = 202;
/**
* @var int
*/
public const NON_AUTHORITATIVE_INFORMATION = 203;
/**
* @var int
*/
public const NO_CONTENT = 204;
/**
* @var int
*/
public const RESET_CONTENT = 205;
/**
* @var int
*/
public const PARTIAL_CONTENT = 206;
/**
* @var int
*/
public const MULTI_STATUS = 207; // RFC4918
/**
* @var int
*/
public const ALREADY_REPORTED = 208; // RFC5842
/**
* @var int
*/
public const IM_USED = 226; // RFC3229
/**
* @var int
*/
public const MULTIPLE_CHOICES = 300;
/**
* @var int
*/
public const MOVED_PERMANENTLY = 301;
/**
* @var int
*/
public const FOUND = 302;
/**
* @var int
*/
public const SEE_OTHER = 303;
/**
* @var int
*/
public const NOT_MODIFIED = 304;
/**
* @var int
*/
public const USE_PROXY = 305;
/**
* @var int
*/
public const RESERVED = 306;
/**
* @var int
*/
public const TEMPORARY_REDIRECT = 307;
/**
* @var int
*/
public const PERMANENTLY_REDIRECT = 308; // RFC7238
/**
* @var int
*/
public const BAD_REQUEST = 400;
/**
* @var int
*/
public const UNAUTHORIZED = 401;
/**
* @var int
*/
public const PAYMENT_REQUIRED = 402;
/**
* @var int
*/
public const FORBIDDEN = 403;
/**
* @var int
*/
public const NOT_FOUND = 404;
/**
* @var int
*/
public const METHOD_NOT_ALLOWED = 405;
/**
* @var int
*/
public const NOT_ACCEPTABLE = 406;
/**
* @var int
*/
public const PROXY_AUTHENTICATION_REQUIRED = 407;
/**
* @var int
*/
public const REQUEST_TIMEOUT = 408;
/**
* @var int
*/
public const CONFLICT = 409;
/**
* @var int
*/
public const GONE = 410;
/**
* @var int
*/
public const LENGTH_REQUIRED = 411;
/**
* @var int
*/
public const PRECONDITION_FAILED = 412;
/**
* @var int
*/
public const REQUEST_ENTITY_TOO_LARGE = 413;
/**
* @var int
*/
public const REQUEST_URI_TOO_LONG = 414;
/**
* @var int
*/
public const UNSUPPORTED_MEDIA_TYPE = 415;
/**
* @var int
*/
public const REQUESTED_RANGE_NOT_SATISFIABLE = 416;
/**
* @var int
*/
public const EXPECTATION_FAILED = 417;
/**
* @var int
*/
public const I_AM_A_TEAPOT = 418; // RFC2324
/**
* @var int
*/
public const MISDIRECTED_REQUEST = 421; // RFC7540
/**
* @var int
*/
public const UNPROCESSABLE_ENTITY = 422; // RFC4918
/**
* @var int
*/
public const LOCKED = 423; // RFC4918
/**
* @var int
*/
public const FAILED_DEPENDENCY = 424; // RFC4918
/**
* @var int
*/
public const RESERVED_FOR_WEBDAV_ADVANCED_COLLECTIONS_EXPIRED_PROPOSAL = 425; // RFC2817
/**
* @var int
*/
public const UPGRADE_REQUIRED = 426; // RFC2817
/**
* @var int
*/
public const PRECONDITION_REQUIRED = 428; // RFC6585
/**
* @var int
*/
public const TOO_MANY_REQUESTS = 429; // RFC6585
/**
* @var int
*/
public const REQUEST_HEADER_FIELDS_TOO_LARGE = 431; // RFC6585
/**
* @var int
*/
public const UNAVAILABLE_FOR_LEGAL_REASONS = 451;
/**
* @var int
*/
public const INTERNAL_SERVER_ERROR = 500;
/**
* @var int
*/
public const NOT_IMPLEMENTED = 501;
/**
* @var int
*/
public const BAD_GATEWAY = 502;
/**
* @var int
*/
public const SERVICE_UNAVAILABLE = 503;
/**
* @var int
*/
public const GATEWAY_TIMEOUT = 504;
/**
* @var int
*/
public const VERSION_NOT_SUPPORTED = 505;
/**
* @var int
*/
public const VARIANT_ALSO_NEGOTIATES_EXPERIMENTAL = 506; // RFC2295
/**
* @var int
*/
public const INSUFFICIENT_STORAGE = 507; // RFC4918
/**
* @var int
*/
public const LOOP_DETECTED = 508; // RFC5842
/**
* @var int
*/
public const NOT_EXTENDED = 510; // RFC2774
/**
* @var int
*/
public const NETWORK_AUTHENTICATION_REQUIRED = 511; // RFC6585
/**
* @var array<int, string>
*/
private static array $codes = [
100 => 'Continue',
102 => 'Processing',
103 => 'Early Hints',
200 => 'OK',
201 => 'Created',
202 => 'Accepted',
203 => 'Non-Authoritative Information',
204 => 'No Content',
205 => 'Reset Content',
206 => 'Partial Content',
207 => 'Multi-Status',
208 => 'Already Reported',
226 => 'IM Used',
300 => 'Multiple Choices',
301 => 'Moved Permanently',
302 => 'Found',
303 => 'See Other',
304 => 'Not Modified',
305 => 'Use Proxy',
306 => 'Reserved',
307 => 'Temporary Redirect',
308 => 'Permanent Redirect',
400 => 'Bad Request',
401 => 'Unauthorized',
402 => 'Payment Required',
403 => 'Forbidden',
404 => 'Not Found',
405 => 'Method Not Allowed',
406 => 'Not Acceptable',
407 => 'Proxy Authentication Required',
408 => 'Request Timeout',
409 => 'Conflict',
410 => 'Gone',
411 => 'Length Required',
412 => 'Precondition Failed',
413 => 'Request Entity Too Large',
414 => 'Request-URI Too Long',
415 => 'Unsupported Media Type',
416 => 'Requested Range Not Satisfiable',
417 => 'Expectation Failed',
418 => 'Unassigned',
421 => 'Misdirected Request',
422 => 'Unprocessable Entity',
423 => 'Locked',
424 => 'Failed Dependency',
425 => 'Too Early',
426 => 'Upgrade Required',
428 => 'Precondition Required',
429 => 'Too Many Requests',
431 => 'Request Header Fields Too Large',
451 => 'Unavailable For Legal Reasons',
500 => 'Internal Server Error',
501 => 'Not Implemented',
502 => 'Bad Gateway',
503 => 'Service Unavailable',
504 => 'Gateway Timeout',
505 => 'HTTP Version Not Supported',
506 => 'Variant Also Negotiates',
507 => 'Insufficient Storage',
508 => 'Loop Detected',
510 => 'Not Extended',
511 => 'Network Authentication Required'
];
/**
* Returns string with HTTP code and its description
*
* ```php
* <?php
* HttpCode::getDescription(200); // '200 (OK)'
* HttpCode::getDescription(401); // '401 (Unauthorized)'
* ```
*/
public static function getDescription(int $code): int|string
{
if (isset(self::$codes[$code])) {
return sprintf('%d (%s)', $code, self::$codes[$code]);
}
return $code;
}
}