├── .gitignore ├── .scrutinizer.yml ├── .travis.yml ├── LICENSE ├── README.md ├── certificate.pem ├── codeception.yml ├── composer.json ├── examples ├── fast_hello.php ├── hello.php └── upload_form.php ├── hyper-run ├── socket ├── LICENSE ├── README.md └── src │ ├── Connection.php │ ├── ConnectionException.php │ ├── ConnectionInterface.php │ ├── SecureConnection.php │ ├── Server.php │ └── ServerInterface.php ├── src ├── BuiltinServerFactory.php ├── Internal │ ├── BuiltinServer.php │ ├── ConnectionHandler.php │ ├── Queue.php │ └── Sink.php └── Master.php └── tests ├── _bootstrap.php ├── _data └── dump.sql ├── _output └── .gitignore ├── _support ├── AcceptanceTester.php ├── FunctionalTester.php ├── Helper │ ├── Acceptance.php │ ├── Functional.php │ └── Unit.php ├── UnitTester.php └── _generated │ ├── AcceptanceTesterActions.php │ ├── FunctionalTesterActions.php │ └── UnitTesterActions.php ├── acceptance.suite.yml ├── acceptance └── _bootstrap.php ├── functional.suite.yml ├── functional └── _bootstrap.php ├── unit.suite.yml └── unit ├── RemoteSimpleTest.php └── _bootstrap.php /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor/ 2 | /composer.lock 3 | 4 | tests/_output/* 5 | -------------------------------------------------------------------------------- /.scrutinizer.yml: -------------------------------------------------------------------------------- 1 | checks: 2 | php: 3 | code_rating: true 4 | duplication: false 5 | 6 | filter: 7 | excluded_paths: 8 | - tests/* 9 | - examples/* 10 | - vendor/* 11 | - socket/* 12 | - hyper-run 13 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | php: 4 | - 7.0.8 5 | 6 | install: 7 | - composer install 8 | - ./vendor/bin/codecept build 9 | 10 | sudo: false 11 | 12 | cache: 13 | directories: 14 | - ./vendor 15 | - $HOME/.composer/cache 16 | 17 | script: 18 | - ./hyper-run -S localhost:8000 -s localhost:44300 -n 5 -t examples & 19 | - vendor/bin/codecept run unit 20 | - kill $! 21 | 22 | branches: 23 | only: 24 | - master 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 mpyw 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PHP Hyper Built-in Server [![Build Status](https://travis-ci.com/mpyw/php-hyper-builtin-server.svg?branch=master)](https://travis-ci.com/mpyw/php-hyper-builtin-server) [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/mpyw/php-hyper-builtin-server/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/mpyw/php-hyper-builtin-server/?branch=master) 2 | 3 | Reverse proxy for PHP built-in server which supports multiprocessing and TLS/SSL encryption. 4 | 5 | ## Installing 6 | 7 | ### Global install 8 | 9 | ```shell script 10 | composer global require mpyw/php-hyper-builtin-server:^3.0 11 | ``` 12 | 13 | If not yet, you must add **`~/.composer/vendor/bin`** to `$PATH`. 14 | Append the following statement to `~/.bashrc`, `~/.zshrc` or what not. 15 | 16 | ```bash 17 | export PATH="$HOME/.composer/vendor/bin:$PATH" 18 | ``` 19 | 20 | ### Local install only for development environment 21 | 22 | ```shell script 23 | composer require --dev mpyw/php-hyper-builtin-server:^3.0 24 | ``` 25 | 26 | Use **`vendor/bin/hyper-run`** as the execution path. 27 | 28 | ## Usage 29 | 30 | ### Quick start 31 | 32 | ```shell script 33 | hyper-run -S localhost -s localhost -t src/app/www 34 | ``` 35 | 36 | 2 servers will start with the directory `src/app/www` as the document root: 37 | 38 | - `http://localhost:8000` 39 | - `https://localhost:44300` 40 | 41 | Servers start with first unoccupied port within range depending on a scheme. 42 | 43 | | Scheme | Default | Range | 44 | | ------- | ------- | ----------- | 45 | | `HTTP` | 8000 | 8000-8099 | 46 | | `HTTPS` | 44300 | 44300-44399 | 47 | 48 | ### Customize ports 49 | 50 | ```shell script 51 | hyper-run -S localhost:8080 -s localhost:4000 -t src/app/www 52 | ``` 53 | 54 | 2 servers will start with the directory `src/app/www` as the document root: 55 | 56 | - `http://localhost:8080` 57 | - `https://localhost:4000` 58 | 59 | ### Command Reference 60 | 61 | ```ShellSession 62 | mpyw@localhost:~$ hyper-run -h 63 | 64 | Usage: 65 | hyper-run 66 | 67 | Example: 68 | hyper-run -S localhost:8000 -s localhost:44300 69 | 70 | [Required] 71 | -S ":" of an HTTP server. Multiple arguments can be accepted. 72 | -s ":" of an HTTPS server. Multiple arguments can be accepted. 73 | 74 | [Optional] 75 | -n The number of PHP built-in server clusters, from 1 to 20. Default is 10. 76 | -t Path for the document root. Default is the current directory. 77 | -r Path for the router script. Default is empty. 78 | -c Path for the PEM-encoded certificate. 79 | Default is "/Users/mpyw/.composer/vendor/mpyw/php-hyper-builtin-server/certificate.pem". 80 | 81 | Restrictions: 82 | - The option -s is only supported on PHP 5.6.0 or later. 83 | - Access logs will not be displayed on Windows. 84 | 85 | mpyw@localhost:~$ 86 | ``` 87 | 88 | ## Note for Windows users 89 | 90 | Unfortunately, `cmd.exe` has no option to run via shebang `#!/usr/bin/env php`, so you need to create the following batch file in the proper directory. 91 | 92 | ### For Standalone PHP 93 | 94 | ```bat 95 | @echo OFF 96 | "C:\php\php.exe" "%HOMEPATH%\.composer\vendor\mpyw\php-hyper-builtin-server\hyper-run" %* 97 | ``` 98 | 99 | ### For XAMPP 100 | 101 | ```bat 102 | @echo OFF 103 | "C:\xampp\php\php.exe" "%HOMEPATH%\.composer\vendor\mpyw\php-hyper-builtin-server\hyper-run" %* 104 | ``` 105 | 106 | ## License 107 | 108 | - `PHP Hyper Built-in Server` is open-sourced software licensed under the [MIT license](LICENSE) by [@mpyw](https://github.com/mpyw). 109 | -------------------------------------------------------------------------------- /certificate.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDLzCCAhegAwIBAgIBATANBgkqhkiG9w0BAQsFADAwMQswCQYDVQQGEwJKUDEN 3 | MAsGA1UECgwETnlhbjESMBAGA1UEAwwJbG9jYWxob3N0MCAXDTE2MDgzMTA4MDAw 4 | MFoYDzk5OTkxMjMxMjM1OTU5WjAwMQswCQYDVQQGEwJKUDENMAsGA1UECgwETnlh 5 | bjESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB 6 | CgKCAQEAxQfnqX86T5NIXG6SqdqkHy+UxuUZMdwaBgaoN5p4aF2ZUF6jCax0ahEB 7 | 7QMvK7I8/mlImQgEwIWIzKMfvxGCxkEyjN+N+CnkkghFwVLBzHPVhAuP2O8kjySG 8 | z7Ah4VKh2AMylLcvuvay3i/jNjrSH3R7QwTFMLk3+d0a28tBEVYPu9+6AdC0KMMa 9 | s4ZBX7IGd8dDcgbaAH6SlzN/RK3m73MFG/2pJzUmQDge4ovkgJMuel73rwmqGAeM 10 | zHvxLmHo76Pt/oQHyUtSAD0M7G+4rWuOOoML45sjfR+6V/0U+a96+LIUZy1bhGKC 11 | J2b1SMEtT63oFMmn/91gRtEJOcI0DQIDAQABo1IwUDAOBgNVHQ8BAf8EBAMCBaAw 12 | HQYDVR0OBBYEFLiMWjpdG2ApFSscfZoU1D9y/0RKMB8GA1UdIwQYMBaAFLiMWjpd 13 | G2ApFSscfZoU1D9y/0RKMA0GCSqGSIb3DQEBCwUAA4IBAQCSLcUF3q4gwqmGNwdv 14 | E9/VHWExHG/DKtuDFyoQhYPbAZRTpWxs9lDUbVKygS5crbA7vZPrJrwEBhmKR1z2 15 | WEizlqb/J5DN6X9y9R7B+FDhE/WHYtvuLEBDxHrpc9p7bAIEb6vEH8iV0RryDc+c 16 | T+tDjWZAgQbxx/50Ybb2buodUcZQ9PRdaPrkfy7Y7HPRO2U4FpT0HeqGIZirPNGl 17 | bz2AXtwfT4j9tZ5FcMt3UTb23jBS3s6LnOHf3prwcmJNVv7MkQiwkOivJpjhAC7f 18 | +yFaqunQiQnYJAZ1nVk5lt4JyMFK21DvWesSH/rsIDCn5K3rEMo8qonPt8irCpNM 19 | 7oFq 20 | -----END CERTIFICATE----- 21 | -----BEGIN RSA PRIVATE KEY----- 22 | MIIEowIBAAKCAQEAxQfnqX86T5NIXG6SqdqkHy+UxuUZMdwaBgaoN5p4aF2ZUF6j 23 | Cax0ahEB7QMvK7I8/mlImQgEwIWIzKMfvxGCxkEyjN+N+CnkkghFwVLBzHPVhAuP 24 | 2O8kjySGz7Ah4VKh2AMylLcvuvay3i/jNjrSH3R7QwTFMLk3+d0a28tBEVYPu9+6 25 | AdC0KMMas4ZBX7IGd8dDcgbaAH6SlzN/RK3m73MFG/2pJzUmQDge4ovkgJMuel73 26 | rwmqGAeMzHvxLmHo76Pt/oQHyUtSAD0M7G+4rWuOOoML45sjfR+6V/0U+a96+LIU 27 | Zy1bhGKCJ2b1SMEtT63oFMmn/91gRtEJOcI0DQIDAQABAoIBAHpJ4bsNwD9Lhon4 28 | BwdtMJg6i/i2kOClX3GGCDoaockE7vc3BbInW0nJrIxSgaB5S+oLpod0tp5XQwTf 29 | 6gBI+gXayWkuB4uTVM5eXA0VNDuVWVxPYMSgCOzfGt0k0KJtCw5rTaWH3RQJshK9 30 | XiR2dZwYS7jY6RrImrl2RglNKeF7lT2eOwgA3TyHEempRn5ft/IzpiGkLWR1Wrtb 31 | 5Mg+sPaBrPsRUHYdp3QZI4HiRKlpUC2UtLmWD61H9mPQWNUhe7MpqS7MED045wOP 32 | UEnt2inr80QwMkDNLMtXE3rc59nllLNV4l2FNMpCYkTXadHAbLZYmN84iE2Lk3cF 33 | WK4wSPECgYEA8so4urw1YYe8kzOXXa/s4Z79X91P1St7YJZZqdCPwWDAe3hBhdrs 34 | XgbZBI0TBZL5JD4Un3p9k3kZ/EuPSmhz1jctvwXBkzRyraHyMx4ni8tT8dgjJUoa 35 | KVTC5kP2Ha4Dw4OWRBcvxEiYd9IpD7DCUEgj4FZQnbMhy1ptudebbX8CgYEAz8BQ 36 | RElVTfaEL28/XxguIeOqpTC1AtBp1km291Zwti17W7duoN9KVUQeB3WIid2HJik6 37 | SznXbYcKI8A/EaE8b6+kWfeLGkWwtre5EOFABKfZphnMvXu51iEwTVTzL9hcwL24 38 | kJ/ltfMqdJ/zAtBlyWA4i48ixSHWCKjdW7JZ/HMCgYA0mwxfqZgHYdnCK1OIwS2a 39 | VVChDNrXXWyGKQ2UddQwGj6aghvwXNcKKIGtMsQEWSwSZkEhrPC4m2y99cE+ZhNz 40 | PtFyqlSfCKYwaP6JhOccqxmtNR+oXL3+Zpc0PVV+aM339vMAHeYDqZcfnzG2bJ4t 41 | tvPPD+S3xrBz10wfEFBlEQKBgF/KYrg5v/WEtwpQFz43nf3ORb6JKM01X7eIoa6F 42 | 0s6NRhXSDFZs3o9WKypxTRWUaV51IAXTk7tSyqAVYn80gvNUX0mEVEU24PY+3Dwm 43 | QChlZHur5bVwuQ08nFLr2n2zG4FbeSBmObqddnPz1E713BjwHuJqizOuGExB4F8J 44 | a2h1AoGBALGOqK3qG2vuAb3E5O5VhVqkRjA2Q/v+kFl4s8GKMmGrKnzKTWyRu85c 45 | BfmFQ/VpGeKXG1wuf0JQqRVQ3fcIVRK5wNGSp/ozOmEcrQi6qM4cOfMzzrXrha5h 46 | Qnpk0dp7qIzas7N2uXTPMywFN++6lkbYmpZ7SfEeiDFN4zRDikrH 47 | -----END RSA PRIVATE KEY----- 48 | -------------------------------------------------------------------------------- /codeception.yml: -------------------------------------------------------------------------------- 1 | actor: Tester 2 | paths: 3 | tests: tests 4 | log: tests/_output 5 | data: tests/_data 6 | support: tests/_support 7 | envs: tests/_envs 8 | settings: 9 | bootstrap: _bootstrap.php 10 | colors: true 11 | memory_limit: 1024M 12 | extensions: 13 | enabled: 14 | - Codeception\Extension\RunFailed 15 | modules: 16 | config: 17 | Db: 18 | dsn: '' 19 | user: '' 20 | password: '' 21 | dump: tests/_data/dump.sql 22 | 23 | coverage: 24 | whitelist: 25 | include: 26 | - src/* 27 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mpyw/php-hyper-builtin-server", 3 | "description": "Reverse proxy for PHP built-in server which supports multiprocessing and TLS/SSL encryption", 4 | "type": "project", 5 | "license": "MIT", 6 | "keywords": [ 7 | "built-in", 8 | "builtin", 9 | "reverse", 10 | "proxy", 11 | "server", 12 | "TLS", 13 | "SSL", 14 | "multiprocess", 15 | "multiprocessing" 16 | ], 17 | "authors": [ 18 | { 19 | "name": "mpyw", 20 | "email": "ryosuke_i_628@yahoo.co.jp", 21 | "role": "Developer" 22 | }, 23 | { 24 | "name": "Anton Komarev", 25 | "email": "anton@komarev.com", 26 | "homepage": "https://komarev.com", 27 | "role": "Developer" 28 | } 29 | ], 30 | "autoload": { 31 | "psr-4": { 32 | "mpyw\\HyperBuiltinServer\\": "src/", 33 | "React\\Socket\\": "socket/src/" 34 | } 35 | }, 36 | "require": { 37 | "php": ">=5.4.0", 38 | "lib-openssl": "*", 39 | "react/child-process": "^0.4.1", 40 | "react/promise": "^2.4" 41 | }, 42 | "require-dev": { 43 | "php": ">=7.0.0", 44 | "codeception/aspect-mock": "^2.0", 45 | "codeception/codeception": "^2.2", 46 | "codeception/specify": "^0.4.6", 47 | "satooshi/php-coveralls": "^1.0", 48 | "mpyw/privator": "^2.0", 49 | "mpyw/co": "^1.5" 50 | }, 51 | "bin": ["hyper-run"] 52 | } 53 | -------------------------------------------------------------------------------- /examples/fast_hello.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |
SUCCESS
8 | 9 |
ERROR:
10 | 11 | 12 | -------------------------------------------------------------------------------- /hyper-run: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | 31 | 32 | Example: 33 | $_SERVER[SCRIPT_NAME] -S localhost:8000 -s localhost:44300 34 | 35 | [Required] 36 | -S \":\" of an HTTP server. Multiple arguments can be accepted. 37 | -s \":\" of an HTTPS server. Multiple arguments can be accepted. 38 | 39 | [Optional] 40 | -n The number of PHP built-in server clusters, from 1 to 20. Default is 10. 41 | -t Path for the document root. Default is the current directory. 42 | -r Path for the router script. Default is empty. 43 | -c Path for the PEM-encoded certificate. 44 | Default is \"$cert\". 45 | 46 | Restriction: 47 | - The option -s is only supported on PHP 5.6.0 or later. 48 | - Access logs will not be displayed on Windows. 49 | 50 | " 51 | ); 52 | } 53 | 54 | $options = getopt('S:s:n:t:r:c:h'); 55 | 56 | $help = isset($options['h']); 57 | $servers = isset($options['S']) ? (array)$options['S'] : []; 58 | $secures = isset($options['s']) ? (array)$options['s'] : []; 59 | $docroot = isset($options['t']) ? current((array)$options['t']) : null; 60 | $number = isset($options['n']) ? current((array)$options['n']) : '10'; 61 | $router = isset($options['r']) ? current((array)$options['r']) : null; 62 | $cert = isset($options['c']) ? current((array)$options['c']) : (__DIR__ . '/certificate.pem'); 63 | 64 | if ($help) { 65 | usage(); 66 | exit(0); 67 | } 68 | 69 | if (!$servers && !$secures) { 70 | fwrite(STDERR, "Error: At least 1 server must be specified.\n"); 71 | usage(); 72 | exit(1); 73 | } 74 | 75 | function startListener($master, $listener) 76 | { 77 | $host = sprintf( 78 | '%s://%s:%s', 79 | $listener[2] ? 'https' : 'http', $listener[0], $listener[1] 80 | ); 81 | fwrite(STDOUT, "Development server started: <{$host}>\n"); 82 | 83 | try { 84 | call_user_func_array([$master, 'addListener'], $listener); 85 | } catch (React\Socket\ConnectionException $exception) { 86 | $reason = 'Address already in use'; 87 | 88 | $isPortSpecified = $listener[4]; 89 | 90 | if ($isPortSpecified || strpos($exception->getMessage(), $reason) === false) { 91 | throw $exception; 92 | } 93 | 94 | fwrite(STDERR, sprintf( 95 | "Failed to listen on %s:%s (reason: %s)\n", 96 | $listener[0], $listener[1], $reason 97 | )); 98 | 99 | $listener[1] = (int) $listener[1] + 1; 100 | startListener($master, $listener); 101 | } 102 | } 103 | 104 | try { 105 | 106 | if (!ctype_digit($number) || $number < 1 || $number > 20) { 107 | throw new \RuntimeException('The number of clusters must be between 1 and 20.'); 108 | } 109 | if ($docroot !== null && !is_dir($docroot)) { 110 | throw new \RuntimeException("No such document root directory: $docroot"); 111 | } 112 | if ($router !== null && !is_file($router)) { 113 | throw new \RuntimeException("No such router script file: $router"); 114 | } 115 | if (!is_file($cert)) { 116 | throw new \RuntimeException("No such certificate file: $cert"); 117 | } 118 | if (!openssl_pkey_get_public("file://$cert")) { 119 | throw new \RuntimeException("Invalid certificate file: $cert"); 120 | } 121 | 122 | $listeners = []; 123 | foreach ([$servers, $secures] as $type => $group) { 124 | $isSecure = $type === 1; 125 | foreach ($group as $i => $server) { 126 | list($host, $port) = explode(':', $server, 2) + [1 => '']; 127 | 128 | if ($port === '') { 129 | $port = $isSecure ? '44300' : '8000'; 130 | $isPortSpecified = false; 131 | } else { 132 | $isPortSpecified = true; 133 | } 134 | 135 | $ip = filter_var(gethostbyname($host), FILTER_VALIDATE_IP, FILTER_FLAG_IPV4); 136 | $regex = '/\A(?:[0-9]{1,4}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])\z/'; 137 | if ($ip === false || !preg_match($regex, $port)) { 138 | throw new \RuntimeException("Invalid host or port: $server"); 139 | } 140 | if (isset($listeners[$server])) { 141 | throw new \RuntimeException("Duplicated entry: $server"); 142 | } 143 | $listeners[$server] = [$host, $port, $isSecure, $cert, $isPortSpecified]; 144 | } 145 | } 146 | 147 | $loop = React\EventLoop\Factory::create(); 148 | 149 | $usedProcesses = []; 150 | $factory = new mpyw\HyperBuiltinServer\BuiltinServerFactory($loop); 151 | $factory 152 | ->createMultipleAsync($number, '127.0.0.1', $docroot, $router) 153 | ->then(function (array $processes) use ($loop, $listeners, &$usedProcesses) { 154 | $usedProcesses = $processes; 155 | $master = new mpyw\HyperBuiltinServer\Master($loop, $processes); 156 | foreach ($listeners as $listener) { 157 | startListener($master, $listener); 158 | } 159 | }) 160 | ->then(null, function ($e) use (&$usedProcesses) { 161 | foreach ($usedProcesses as $process) { 162 | $process->terminate(); 163 | } 164 | throw $e; 165 | }) 166 | ->done(); 167 | 168 | set_time_limit(0); 169 | $loop->run(); 170 | 171 | } catch (\Throwable $e) { 172 | 173 | fwrite(STDERR, "Error: {$e->getMessage()}\n"); 174 | exit(1); 175 | 176 | } catch (\Exception $e) { 177 | 178 | fwrite(STDERR, "Error: {$e->getMessage()}\n"); 179 | exit(1); 180 | 181 | } 182 | -------------------------------------------------------------------------------- /socket/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 Igor Wiedler, Chris Boden 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is furnished 8 | to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /socket/README.md: -------------------------------------------------------------------------------- 1 | # Socket Component 2 | 3 | [![Build Status](https://secure.travis-ci.com/reactphp/socket.png?branch=master)](http://travis-ci.com/reactphp/socket) 4 | 5 | Library for building an evented socket server. 6 | 7 | The socket component provides a more usable interface for a socket-layer 8 | server or client based on the [`EventLoop`](https://github.com/reactphp/event-loop) 9 | and [`Stream`](https://github.com/reactphp/stream) components. 10 | 11 | **Table of Contents** 12 | 13 | * [Quickstart example](#quickstart-example) 14 | * [Usage](#usage) 15 | * [Server](#server) 16 | * [Connection](#connection) 17 | * [Install](#install) 18 | * [License](#license) 19 | 20 | ## Quickstart example 21 | 22 | Here is a server that closes the connection if you send it anything: 23 | 24 | ```php 25 | $loop = React\EventLoop\Factory::create(); 26 | 27 | $socket = new React\Socket\Server($loop); 28 | $socket->on('connection', function ($conn) { 29 | $conn->write("Hello there!\n"); 30 | $conn->write("Welcome to this amazing server!\n"); 31 | $conn->write("Here's a tip: don't say anything.\n"); 32 | 33 | $conn->on('data', function ($data) use ($conn) { 34 | $conn->close(); 35 | }); 36 | }); 37 | $socket->listen(1337); 38 | 39 | $loop->run(); 40 | ``` 41 | 42 | You can change the host the socket is listening on through a second parameter 43 | provided to the listen method: 44 | 45 | ```php 46 | $socket->listen(1337, '192.168.0.1'); 47 | ``` 48 | 49 | Here's a client that outputs the output of said server and then attempts to 50 | send it a string. 51 | For anything more complex, consider using the 52 | [`SocketClient`](https://github.com/reactphp/socket-client) component instead. 53 | 54 | ```php 55 | $loop = React\EventLoop\Factory::create(); 56 | 57 | $client = stream_socket_client('tcp://127.0.0.1:1337'); 58 | $conn = new React\Stream\Stream($client, $loop); 59 | $conn->pipe(new React\Stream\Stream(STDOUT, $loop)); 60 | $conn->write("Hello World!\n"); 61 | 62 | $loop->run(); 63 | ``` 64 | 65 | ## Usage 66 | 67 | ### Server 68 | 69 | The server can listen on a port and will emit a `connection` event whenever a 70 | client connects. 71 | 72 | ### Connection 73 | 74 | The `Connection` is a readable and writable [`Stream`](https://github.com/reactphp/stream). 75 | The incoming connection represents the server-side end of the connection. 76 | 77 | It MUST NOT be used to represent an outgoing connection in a client-side context. 78 | If you want to establish an outgoing connection, 79 | use the [`SocketClient`](https://github.com/reactphp/socket-client) component instead. 80 | 81 | ## Install 82 | 83 | The recommended way to install this library is [through Composer](http://getcomposer.org). 84 | [New to Composer?](http://getcomposer.org/doc/00-intro.md) 85 | 86 | This will install the latest supported version: 87 | 88 | ```bash 89 | $ composer require react/socket:~0.4.0 90 | ``` 91 | 92 | If you care a lot about BC, you may also want to look into supporting legacy versions: 93 | 94 | ```bash 95 | $ composer require "react/socket:~0.4.0|~0.3.0" 96 | ``` 97 | 98 | More details and upgrade guides can be found in the [CHANGELOG](CHANGELOG.md). 99 | 100 | ## License 101 | 102 | MIT, see [LICENSE file](LICENSE). 103 | -------------------------------------------------------------------------------- /socket/src/Connection.php: -------------------------------------------------------------------------------- 1 | bufferSize); 14 | if ('' !== $data && false !== $data) { 15 | $this->emit('data', array($data, $this)); 16 | } 17 | 18 | if ('' === $data || false === $data || !is_resource($stream) || feof($stream)) { 19 | $this->end(); 20 | } 21 | } 22 | 23 | public function handleClose() 24 | { 25 | if (is_resource($this->stream)) { 26 | // http://chat.stackoverflow.com/transcript/message/7727858#7727858 27 | stream_socket_shutdown($this->stream, STREAM_SHUT_RDWR); 28 | stream_set_blocking($this->stream, false); 29 | fclose($this->stream); 30 | } 31 | } 32 | 33 | public function getRemoteAddress() 34 | { 35 | return $this->parseAddress(stream_socket_get_name($this->stream, true)); 36 | } 37 | 38 | private function parseAddress($address) 39 | { 40 | return trim(substr($address, 0, strrpos($address, ':')), '[]'); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /socket/src/ConnectionException.php: -------------------------------------------------------------------------------- 1 | isSecure) { 16 | $enabled = @stream_socket_enable_crypto($stream, true, $this->protocolNumber); 17 | if ($enabled === false) { 18 | $this 19 | ->err('Failed to complete a secure handshake with the client.') 20 | ->end() 21 | ; 22 | return; 23 | } elseif ($enabled === 0) { 24 | return; 25 | } 26 | $this->isSecure = true; 27 | $this->emit('connection', array($this)); 28 | $scope = $this; 29 | $this->on('close', function () use ($scope, $stream) { 30 | if (false === stream_socket_enable_crypto($stream, false)) { 31 | $scope->err('Failed to gracefully shutdown a secure connection.'); 32 | } 33 | }); 34 | } 35 | 36 | $data = fread($stream, $this->bufferSize); 37 | 38 | if ('' !== $data && false !== $data) { 39 | $this->emit('data', array($data, $this)); 40 | } 41 | 42 | if (false === $data || !is_resource($stream) || feof($stream)) { 43 | $this->end(); 44 | } 45 | } 46 | 47 | /** 48 | * Set the STREAM_CRYPTO_METHOD_*_SERVER flags suitable for enabling TLS on 49 | * the socket stream handled by this connection. 50 | * 51 | * @param int $protocolNumber 52 | */ 53 | public function setProtocol($protocolNumber = null) 54 | { 55 | $this->protocolNumber = $protocolNumber; 56 | } 57 | 58 | private function err($message) 59 | { 60 | $this->emit('error', array(new \RuntimeException($message))); 61 | return $this; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /socket/src/Server.php: -------------------------------------------------------------------------------- 1 | loop = $loop; 17 | } 18 | 19 | public function listen($port, $host = '127.0.0.1', $streamContext = array()) 20 | { 21 | if (isset($streamContext['ssl']) && PHP_VERSION_ID < 50600) { 22 | throw new \RuntimeException( 23 | 'Secure connections are not available before PHP 5.6.0' 24 | ); 25 | } 26 | 27 | if (strpos($host, ':') !== false) { 28 | // enclose IPv6 addresses in square brackets before appending port 29 | $host = '[' . $host . ']'; 30 | } 31 | 32 | $this->master = @stream_socket_server( 33 | "tcp://$host:$port", 34 | $errno, 35 | $errstr, 36 | STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, 37 | stream_context_create($streamContext) 38 | ); 39 | if (false === $this->master) { 40 | $message = "Could not bind to tcp://$host:$port: $errstr"; 41 | throw new ConnectionException($message, $errno); 42 | } 43 | stream_set_blocking($this->master, 0); 44 | 45 | $that = $this; 46 | 47 | $this->loop->addReadStream($this->master, function ($master) use ($that) { 48 | $newSocket = @stream_socket_accept($master); 49 | if (false === $newSocket) { 50 | $that->emit('error', array(new \RuntimeException('Error accepting new connection'))); 51 | 52 | return; 53 | } 54 | $that->handleConnection($newSocket); 55 | }); 56 | } 57 | 58 | public function handleConnection($socket) 59 | { 60 | stream_set_blocking($socket, 0); 61 | 62 | $client = $this->createConnection($socket); 63 | 64 | $this->emit('connection', array($client)); 65 | } 66 | 67 | public function getPort() 68 | { 69 | $name = stream_socket_get_name($this->master, false); 70 | 71 | return (int) substr(strrchr($name, ':'), 1); 72 | } 73 | 74 | public function shutdown() 75 | { 76 | $this->loop->removeStream($this->master); 77 | fclose($this->master); 78 | $this->removeAllListeners(); 79 | } 80 | 81 | public function createConnection($socket) 82 | { 83 | $connection = null; 84 | $context = stream_context_get_options($socket); 85 | 86 | if (! isset($context['ssl'])) { 87 | $connection = new Connection($socket, $this->loop); 88 | } else { 89 | $connection = new SecureConnection($socket, $this->loop); 90 | $connection->setProtocol($this->getSecureProtocolNumber($context)); 91 | } 92 | 93 | return $connection; 94 | } 95 | 96 | /** 97 | * Get the STREAM_CRYPTO_METHOD_*_SERVER flags suitable for enabling TLS 98 | * on a server socket. 99 | * 100 | * Used the supplied $streamContext['ssl']['crypto_method'] or a default set 101 | * which will support as many SSL/TLS protocols as possible. 102 | * 103 | * @param array $streamContext 104 | * 105 | * @return int 106 | */ 107 | public function getSecureProtocolNumber($streamContext) 108 | { 109 | if (isset($streamContext['ssl']['crypto_method'])) { 110 | return $streamContext['ssl']['crypto_method']; 111 | } elseif (defined('STREAM_CRYPTO_METHOD_ANY_SERVER')) { 112 | return constant('STREAM_CRYPTO_METHOD_ANY_SERVER'); 113 | } 114 | $protoNum = 0; 115 | if (defined('STREAM_CRYPTO_METHOD_TLSv1_2_SERVER')) { 116 | $protoNum |= constant('STREAM_CRYPTO_METHOD_TLSv1_2_SERVER'); 117 | } 118 | if (defined('STREAM_CRYPTO_METHOD_TLSv1_1_SERVER')) { 119 | $protoNum |= constant('STREAM_CRYPTO_METHOD_TLSv1_1_SERVER'); 120 | } 121 | if (defined('STREAM_CRYPTO_METHOD_TLSv1_0_SERVER')) { 122 | $protoNum |= constant('STREAM_CRYPTO_METHOD_TLSv1_0_SERVER'); 123 | } 124 | if (defined('STREAM_CRYPTO_METHOD_SSLv3_SERVER')) { 125 | $protoNum |= constant('STREAM_CRYPTO_METHOD_SSLv3_SERVER'); 126 | } 127 | if (defined('STREAM_CRYPTO_METHOD_SSLv2_SERVER')) { 128 | $protoNum |= constant('STREAM_CRYPTO_METHOD_SSLv2_SERVER'); 129 | } 130 | return $protoNum; 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /socket/src/ServerInterface.php: -------------------------------------------------------------------------------- 1 | loop = $loop; 20 | $this->stderr = new Stream(fopen('php://stderr', 'wb'), $this->loop); 21 | $this->php = $php_command; 22 | $this->retry = $retry_count; 23 | } 24 | 25 | protected function createInternalAsync($host, $docroot, $router) 26 | { 27 | $deferred = new Deferred; 28 | $process = new BuiltinServer($host, $docroot, $router, $this->php); 29 | 30 | $process->start($this->loop); 31 | $process->on('exit', function ($code) use ($deferred) { 32 | $this->stderr->write("Process exit with code $code\n"); 33 | $deferred->reject(); 34 | }); 35 | 36 | $process->stdin->close(); 37 | $process->stdout->close(); 38 | $process->stderr->on('data', function ($output) use ($deferred) { 39 | $this->stderr->write($output); 40 | $deferred->reject(); 41 | }); 42 | 43 | $timer = new Deferred; 44 | $this->loop->addTimer(0.05, function () use ($timer, $process) { 45 | if (DIRECTORY_SEPARATOR === '\\') { 46 | // Pipes opened by proc_open() can break stream_select() loop in Windows. 47 | // This fix might do the trick... 48 | $process->stderr->close(); 49 | } 50 | $timer->resolve($process); 51 | }); 52 | 53 | return \React\Promise\race([ 54 | $deferred->promise(), 55 | $timer->promise(), 56 | ])->then(null, function () use ($process) { 57 | $process->terminate(); 58 | return new RejectedPromise; 59 | }); 60 | } 61 | 62 | protected function createInternalWithRetryAsync($host, $docroot, $router, $retry) 63 | { 64 | return $this 65 | ->createInternalAsync($host, $docroot, $router) 66 | ->then(null, function () use ($host, $docroot, $router, $retry) { 67 | if ($retry < 1) { 68 | throw new \RuntimeException('Failed to launch server.'); 69 | } 70 | return $this->createInternalWithRetryAsync($host, $docroot, $router, $retry - 1); 71 | }); 72 | } 73 | 74 | public function createAsync($host = '127.0.0.1', $docroot = null, $router = null) 75 | { 76 | return $this->createInternalWithRetryAsync($host, $docroot, $router, $this->retry); 77 | } 78 | 79 | public function createMultipleAsync($n, $host = '127.0.0.1', $docroot = null, $router = null) 80 | { 81 | $promises = []; 82 | for ($i = 0; $i < $n; ++$i) { 83 | $promises[] = $this->createAsync($host, $docroot, $router); 84 | } 85 | return \React\Promise\all($promises); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/Internal/BuiltinServer.php: -------------------------------------------------------------------------------- 1 | true]); 22 | $this->host = $host; 23 | $this->port = $port; 24 | } 25 | 26 | public function getSocketClient() 27 | { 28 | $socket = @stream_socket_client("tcp://{$this->host}:{$this->port}"); 29 | if ($socket === false) { 30 | throw new \RuntimeException(error_get_last()['message']); 31 | } 32 | return $socket; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Internal/ConnectionHandler.php: -------------------------------------------------------------------------------- 1 | master = $master; 16 | } 17 | 18 | public function __invoke(ConnectionInterface $conn) 19 | { 20 | $sink = new Sink($conn); 21 | $this->master->queue->executeAsync(function (BuiltinServer $process) use ($conn, $sink) { 22 | $deferred = new Deferred; 23 | try { 24 | $child = new Stream($process->getSocketClient(), $this->master->loop); 25 | $sink->pipe($child); 26 | $child->pipe($conn); 27 | $child->on('close', function () use ($deferred) { 28 | $deferred->resolve(); 29 | }); 30 | } catch (\RuntimeException $e) { 31 | $conn->write("HTTP/1.0 502 Bad Gateway\r\n"); 32 | $conn->end("\r\n"); 33 | $deferred->resolve(); 34 | } 35 | return $deferred->promise(); 36 | }); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Internal/Queue.php: -------------------------------------------------------------------------------- 1 | processes[spl_object_hash($process)] = $process; 17 | }); 18 | } 19 | 20 | public function executeAsync(\Closure $promisor) 21 | { 22 | $this->processes 23 | ? $this->executeImmediate($promisor) 24 | : $this->executeReserved($promisor); 25 | } 26 | 27 | protected function executeImmediate(\Closure $promisor) 28 | { 29 | $process = current($this->processes); 30 | unset($this->processes[spl_object_hash($process)]); 31 | $promisor($process)->always(function () use ($process) { 32 | $this->processes[spl_object_hash($process)] = $process; 33 | $this->dequeue(); 34 | }); 35 | } 36 | 37 | protected function executeReserved(\Closure $promisor) 38 | { 39 | $this->promisors[] = $promisor; 40 | } 41 | 42 | protected function dequeue() 43 | { 44 | if ($promisor = array_shift($this->promisors)) { 45 | $this->executeImmediate($promisor); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/Internal/Sink.php: -------------------------------------------------------------------------------- 1 | src = $src; 17 | $src->on('data', function ($data) { 18 | $this->buffer .= $data; 19 | if ($this->dst) { 20 | $this->dst->write($this->buffer); 21 | $this->buffer = ''; 22 | } 23 | }); 24 | $src->on('end', function () { 25 | $this->end = true; 26 | if ($this->dst) { 27 | $this->dst->end(); 28 | } 29 | }); 30 | $src->on('error', function () { 31 | $this->end = true; 32 | if ($this->dst) { 33 | $this->dst->end(); 34 | } 35 | }); 36 | } 37 | 38 | public function pipe(WritableStreamInterface $dst) 39 | { 40 | $this->dst = $dst; 41 | if ($this->buffer !== '') { 42 | $this->dst->write($this->buffer); 43 | $this->buffer = ''; 44 | } 45 | if ($this->end) { 46 | $this->dst->end(); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/Master.php: -------------------------------------------------------------------------------- 1 | loop = $loop; 17 | $this->queue = new Queue($processes); 18 | } 19 | 20 | public function addListener($host = '127.0.0.1', $port = 8080, $use_ssl = false, $cert = null) 21 | { 22 | $proxy = new Server($this->loop); 23 | $proxy->on('connection', new ConnectionHandler($this)); 24 | $context = !$use_ssl ? [] : [ 25 | 'ssl' => [ 26 | 'local_cert' => $cert === null ? (__DIR__ . '/../certificate.pem') : $cert, 27 | 'allow_self_signed' => true, 28 | 'verify_peer' => false, 29 | ], 30 | ]; 31 | $proxy->listen($port, $host, $context); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /tests/_bootstrap.php: -------------------------------------------------------------------------------- 1 | init([ 10 | 'debug' => true, 11 | 'includePaths' => [__DIR__ . '/../src'], 12 | ]); 13 | -------------------------------------------------------------------------------- /tests/_data/dump.sql: -------------------------------------------------------------------------------- 1 | /* Replace this file with actual dump of your database */ -------------------------------------------------------------------------------- /tests/_output/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore -------------------------------------------------------------------------------- /tests/_support/AcceptanceTester.php: -------------------------------------------------------------------------------- 1 | getScenario()->runStep(new \Codeception\Step\Action('setHeader', func_get_args())); 30 | } 31 | 32 | 33 | /** 34 | * [!] Method is generated. Documentation taken from corresponding module. 35 | * 36 | * Authenticates user for HTTP_AUTH 37 | * 38 | * @param $username 39 | * @param $password 40 | * @see \Codeception\Module\PhpBrowser::amHttpAuthenticated() 41 | */ 42 | public function amHttpAuthenticated($username, $password) { 43 | return $this->getScenario()->runStep(new \Codeception\Step\Condition('amHttpAuthenticated', func_get_args())); 44 | } 45 | 46 | 47 | /** 48 | * [!] Method is generated. Documentation taken from corresponding module. 49 | * 50 | * Open web page at the given absolute URL and sets its hostname as the base host. 51 | * 52 | * ``` php 53 | * amOnUrl('http://codeception.com'); 55 | * $I->amOnPage('/quickstart'); // moves to http://codeception.com/quickstart 56 | * ?> 57 | * ``` 58 | * @see \Codeception\Module\PhpBrowser::amOnUrl() 59 | */ 60 | public function amOnUrl($url) { 61 | return $this->getScenario()->runStep(new \Codeception\Step\Condition('amOnUrl', func_get_args())); 62 | } 63 | 64 | 65 | /** 66 | * [!] Method is generated. Documentation taken from corresponding module. 67 | * 68 | * Changes the subdomain for the 'url' configuration parameter. 69 | * Does not open a page; use `amOnPage` for that. 70 | * 71 | * ``` php 72 | * amOnSubdomain('user'); 78 | * $I->amOnPage('/'); 79 | * // moves to http://user.mysite.com/ 80 | * ?> 81 | * ``` 82 | * 83 | * @param $subdomain 84 | * 85 | * @return mixed 86 | * @see \Codeception\Module\PhpBrowser::amOnSubdomain() 87 | */ 88 | public function amOnSubdomain($subdomain) { 89 | return $this->getScenario()->runStep(new \Codeception\Step\Condition('amOnSubdomain', func_get_args())); 90 | } 91 | 92 | 93 | /** 94 | * [!] Method is generated. Documentation taken from corresponding module. 95 | * 96 | * Low-level API method. 97 | * If Codeception commands are not enough, use [Guzzle HTTP Client](http://guzzlephp.org/) methods directly 98 | * 99 | * Example: 100 | * 101 | * ``` php 102 | * executeInGuzzle(function (\GuzzleHttp\Client $client) { 104 | * $client->get('/get', ['query' => ['foo' => 'bar']]); 105 | * }); 106 | * ?> 107 | * ``` 108 | * 109 | * It is not recommended to use this command on a regular basis. 110 | * If Codeception lacks important Guzzle Client methods, implement them and submit patches. 111 | * 112 | * @param callable $function 113 | * @see \Codeception\Module\PhpBrowser::executeInGuzzle() 114 | */ 115 | public function executeInGuzzle($function) { 116 | return $this->getScenario()->runStep(new \Codeception\Step\Action('executeInGuzzle', func_get_args())); 117 | } 118 | 119 | 120 | /** 121 | * [!] Method is generated. Documentation taken from corresponding module. 122 | * 123 | * Sets the HTTP header to the passed value - which is used on 124 | * subsequent HTTP requests through PhpBrowser. 125 | * 126 | * Example: 127 | * ```php 128 | * setHeader('X-Requested-With', 'Codeception'); 130 | * $I->amOnPage('test-headers.php'); 131 | * ?> 132 | * ``` 133 | * 134 | * @param string $name the name of the request header 135 | * @param string $value the value to set it to for subsequent 136 | * requests 137 | * @see \Codeception\Lib\InnerBrowser::haveHttpHeader() 138 | */ 139 | public function haveHttpHeader($name, $value) { 140 | return $this->getScenario()->runStep(new \Codeception\Step\Action('haveHttpHeader', func_get_args())); 141 | } 142 | 143 | 144 | /** 145 | * [!] Method is generated. Documentation taken from corresponding module. 146 | * 147 | * Deletes the header with the passed name. Subsequent requests 148 | * will not have the deleted header in its request. 149 | * 150 | * Example: 151 | * ```php 152 | * haveHttpHeader('X-Requested-With', 'Codeception'); 154 | * $I->amOnPage('test-headers.php'); 155 | * // ... 156 | * $I->deleteHeader('X-Requested-With'); 157 | * $I->amOnPage('some-other-page.php'); 158 | * ?> 159 | * ``` 160 | * 161 | * @param string $name the name of the header to delete. 162 | * @see \Codeception\Lib\InnerBrowser::deleteHeader() 163 | */ 164 | public function deleteHeader($name) { 165 | return $this->getScenario()->runStep(new \Codeception\Step\Action('deleteHeader', func_get_args())); 166 | } 167 | 168 | 169 | /** 170 | * [!] Method is generated. Documentation taken from corresponding module. 171 | * 172 | * Opens the page for the given relative URI. 173 | * 174 | * ``` php 175 | * amOnPage('/'); 178 | * // opens /register page 179 | * $I->amOnPage('/register'); 180 | * ``` 181 | * 182 | * @param $page 183 | * @see \Codeception\Lib\InnerBrowser::amOnPage() 184 | */ 185 | public function amOnPage($page) { 186 | return $this->getScenario()->runStep(new \Codeception\Step\Condition('amOnPage', func_get_args())); 187 | } 188 | 189 | 190 | /** 191 | * [!] Method is generated. Documentation taken from corresponding module. 192 | * 193 | * Perform a click on a link or a button, given by a locator. 194 | * If a fuzzy locator is given, the page will be searched for a button, link, or image matching the locator string. 195 | * For buttons, the "value" attribute, "name" attribute, and inner text are searched. 196 | * For links, the link text is searched. 197 | * For images, the "alt" attribute and inner text of any parent links are searched. 198 | * 199 | * The second parameter is a context (CSS or XPath locator) to narrow the search. 200 | * 201 | * Note that if the locator matches a button of type `submit`, the form will be submitted. 202 | * 203 | * ``` php 204 | * click('Logout'); 207 | * // button of form 208 | * $I->click('Submit'); 209 | * // CSS button 210 | * $I->click('#form input[type=submit]'); 211 | * // XPath 212 | * $I->click('//form/*[@type=submit]'); 213 | * // link in context 214 | * $I->click('Logout', '#nav'); 215 | * // using strict locator 216 | * $I->click(['link' => 'Login']); 217 | * ?> 218 | * ``` 219 | * 220 | * @param $link 221 | * @param $context 222 | * @see \Codeception\Lib\InnerBrowser::click() 223 | */ 224 | public function click($link, $context = null) { 225 | return $this->getScenario()->runStep(new \Codeception\Step\Action('click', func_get_args())); 226 | } 227 | 228 | 229 | /** 230 | * [!] Method is generated. Documentation taken from corresponding module. 231 | * 232 | * Checks that the current page contains the given string (case insensitive). 233 | * 234 | * You can specify a specific HTML element (via CSS or XPath) as the second 235 | * parameter to only search within that element. 236 | * 237 | * ``` php 238 | * see('Logout'); // I can suppose user is logged in 240 | * $I->see('Sign Up', 'h1'); // I can suppose it's a signup page 241 | * $I->see('Sign Up', '//body/h1'); // with XPath 242 | * ``` 243 | * 244 | * Note that the search is done after stripping all HTML tags from the body, 245 | * so `$I->see('strong')` will return true for strings like: 246 | * 247 | * - `

I am Stronger than thou

` 248 | * - `` 249 | * 250 | * But will *not* be true for strings like: 251 | * 252 | * - `Home` 253 | * - `
Home` 254 | * - `` 255 | * 256 | * For checking the raw source code, use `seeInSource()`. 257 | * 258 | * @param $text 259 | * @param null $selector 260 | * Conditional Assertion: Test won't be stopped on fail 261 | * @see \Codeception\Lib\InnerBrowser::see() 262 | */ 263 | public function canSee($text, $selector = null) { 264 | return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('see', func_get_args())); 265 | } 266 | /** 267 | * [!] Method is generated. Documentation taken from corresponding module. 268 | * 269 | * Checks that the current page contains the given string (case insensitive). 270 | * 271 | * You can specify a specific HTML element (via CSS or XPath) as the second 272 | * parameter to only search within that element. 273 | * 274 | * ``` php 275 | * see('Logout'); // I can suppose user is logged in 277 | * $I->see('Sign Up', 'h1'); // I can suppose it's a signup page 278 | * $I->see('Sign Up', '//body/h1'); // with XPath 279 | * ``` 280 | * 281 | * Note that the search is done after stripping all HTML tags from the body, 282 | * so `$I->see('strong')` will return true for strings like: 283 | * 284 | * - `

I am Stronger than thou

` 285 | * - `` 286 | * 287 | * But will *not* be true for strings like: 288 | * 289 | * - `Home` 290 | * - `
Home` 291 | * - `` 292 | * 293 | * For checking the raw source code, use `seeInSource()`. 294 | * 295 | * @param $text 296 | * @param null $selector 297 | * @see \Codeception\Lib\InnerBrowser::see() 298 | */ 299 | public function see($text, $selector = null) { 300 | return $this->getScenario()->runStep(new \Codeception\Step\Assertion('see', func_get_args())); 301 | } 302 | 303 | 304 | /** 305 | * [!] Method is generated. Documentation taken from corresponding module. 306 | * 307 | * Checks that the current page doesn't contain the text specified (case insensitive). 308 | * Give a locator as the second parameter to match a specific region. 309 | * 310 | * ```php 311 | * dontSee('Login'); // I can suppose user is already logged in 313 | * $I->dontSee('Sign Up','h1'); // I can suppose it's not a signup page 314 | * $I->dontSee('Sign Up','//body/h1'); // with XPath 315 | * ``` 316 | * 317 | * Note that the search is done after stripping all HTML tags from the body, 318 | * so `$I->dontSee('strong')` will fail on strings like: 319 | * 320 | * - `

I am Stronger than thou

` 321 | * - `` 322 | * 323 | * But will ignore strings like: 324 | * 325 | * - `Home` 326 | * - `
Home` 327 | * - `` 328 | * 329 | * For checking the raw source code, use `seeInSource()`. 330 | * 331 | * @param $text 332 | * @param null $selector 333 | * Conditional Assertion: Test won't be stopped on fail 334 | * @see \Codeception\Lib\InnerBrowser::dontSee() 335 | */ 336 | public function cantSee($text, $selector = null) { 337 | return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('dontSee', func_get_args())); 338 | } 339 | /** 340 | * [!] Method is generated. Documentation taken from corresponding module. 341 | * 342 | * Checks that the current page doesn't contain the text specified (case insensitive). 343 | * Give a locator as the second parameter to match a specific region. 344 | * 345 | * ```php 346 | * dontSee('Login'); // I can suppose user is already logged in 348 | * $I->dontSee('Sign Up','h1'); // I can suppose it's not a signup page 349 | * $I->dontSee('Sign Up','//body/h1'); // with XPath 350 | * ``` 351 | * 352 | * Note that the search is done after stripping all HTML tags from the body, 353 | * so `$I->dontSee('strong')` will fail on strings like: 354 | * 355 | * - `

I am Stronger than thou

` 356 | * - `` 357 | * 358 | * But will ignore strings like: 359 | * 360 | * - `Home` 361 | * - `
Home` 362 | * - `` 363 | * 364 | * For checking the raw source code, use `seeInSource()`. 365 | * 366 | * @param $text 367 | * @param null $selector 368 | * @see \Codeception\Lib\InnerBrowser::dontSee() 369 | */ 370 | public function dontSee($text, $selector = null) { 371 | return $this->getScenario()->runStep(new \Codeception\Step\Assertion('dontSee', func_get_args())); 372 | } 373 | 374 | 375 | /** 376 | * [!] Method is generated. Documentation taken from corresponding module. 377 | * 378 | * Checks that the current page contains the given string in its 379 | * raw source code. 380 | * 381 | * ``` php 382 | * seeInSource('

Green eggs & ham

'); 384 | * ``` 385 | * 386 | * @param $raw 387 | * Conditional Assertion: Test won't be stopped on fail 388 | * @see \Codeception\Lib\InnerBrowser::seeInSource() 389 | */ 390 | public function canSeeInSource($raw) { 391 | return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeInSource', func_get_args())); 392 | } 393 | /** 394 | * [!] Method is generated. Documentation taken from corresponding module. 395 | * 396 | * Checks that the current page contains the given string in its 397 | * raw source code. 398 | * 399 | * ``` php 400 | * seeInSource('

Green eggs & ham

'); 402 | * ``` 403 | * 404 | * @param $raw 405 | * @see \Codeception\Lib\InnerBrowser::seeInSource() 406 | */ 407 | public function seeInSource($raw) { 408 | return $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeInSource', func_get_args())); 409 | } 410 | 411 | 412 | /** 413 | * [!] Method is generated. Documentation taken from corresponding module. 414 | * 415 | * Checks that the current page contains the given string in its 416 | * raw source code. 417 | * 418 | * ```php 419 | * dontSeeInSource('

Green eggs & ham

'); 421 | * ``` 422 | * 423 | * @param $raw 424 | * Conditional Assertion: Test won't be stopped on fail 425 | * @see \Codeception\Lib\InnerBrowser::dontSeeInSource() 426 | */ 427 | public function cantSeeInSource($raw) { 428 | return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeInSource', func_get_args())); 429 | } 430 | /** 431 | * [!] Method is generated. Documentation taken from corresponding module. 432 | * 433 | * Checks that the current page contains the given string in its 434 | * raw source code. 435 | * 436 | * ```php 437 | * dontSeeInSource('

Green eggs & ham

'); 439 | * ``` 440 | * 441 | * @param $raw 442 | * @see \Codeception\Lib\InnerBrowser::dontSeeInSource() 443 | */ 444 | public function dontSeeInSource($raw) { 445 | return $this->getScenario()->runStep(new \Codeception\Step\Assertion('dontSeeInSource', func_get_args())); 446 | } 447 | 448 | 449 | /** 450 | * [!] Method is generated. Documentation taken from corresponding module. 451 | * 452 | * Checks that there's a link with the specified text. 453 | * Give a full URL as the second parameter to match links with that exact URL. 454 | * 455 | * ``` php 456 | * seeLink('Logout'); // matches Logout 458 | * $I->seeLink('Logout','/logout'); // matches Logout 459 | * ?> 460 | * ``` 461 | * 462 | * @param $text 463 | * @param null $url 464 | * Conditional Assertion: Test won't be stopped on fail 465 | * @see \Codeception\Lib\InnerBrowser::seeLink() 466 | */ 467 | public function canSeeLink($text, $url = null) { 468 | return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeLink', func_get_args())); 469 | } 470 | /** 471 | * [!] Method is generated. Documentation taken from corresponding module. 472 | * 473 | * Checks that there's a link with the specified text. 474 | * Give a full URL as the second parameter to match links with that exact URL. 475 | * 476 | * ``` php 477 | * seeLink('Logout'); // matches Logout 479 | * $I->seeLink('Logout','/logout'); // matches Logout 480 | * ?> 481 | * ``` 482 | * 483 | * @param $text 484 | * @param null $url 485 | * @see \Codeception\Lib\InnerBrowser::seeLink() 486 | */ 487 | public function seeLink($text, $url = null) { 488 | return $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeLink', func_get_args())); 489 | } 490 | 491 | 492 | /** 493 | * [!] Method is generated. Documentation taken from corresponding module. 494 | * 495 | * Checks that the page doesn't contain a link with the given string. 496 | * If the second parameter is given, only links with a matching "href" attribute will be checked. 497 | * 498 | * ``` php 499 | * dontSeeLink('Logout'); // I suppose user is not logged in 501 | * $I->dontSeeLink('Checkout now', '/store/cart.php'); 502 | * ?> 503 | * ``` 504 | * 505 | * @param $text 506 | * @param null $url 507 | * Conditional Assertion: Test won't be stopped on fail 508 | * @see \Codeception\Lib\InnerBrowser::dontSeeLink() 509 | */ 510 | public function cantSeeLink($text, $url = null) { 511 | return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeLink', func_get_args())); 512 | } 513 | /** 514 | * [!] Method is generated. Documentation taken from corresponding module. 515 | * 516 | * Checks that the page doesn't contain a link with the given string. 517 | * If the second parameter is given, only links with a matching "href" attribute will be checked. 518 | * 519 | * ``` php 520 | * dontSeeLink('Logout'); // I suppose user is not logged in 522 | * $I->dontSeeLink('Checkout now', '/store/cart.php'); 523 | * ?> 524 | * ``` 525 | * 526 | * @param $text 527 | * @param null $url 528 | * @see \Codeception\Lib\InnerBrowser::dontSeeLink() 529 | */ 530 | public function dontSeeLink($text, $url = null) { 531 | return $this->getScenario()->runStep(new \Codeception\Step\Assertion('dontSeeLink', func_get_args())); 532 | } 533 | 534 | 535 | /** 536 | * [!] Method is generated. Documentation taken from corresponding module. 537 | * 538 | * Checks that current URI contains the given string. 539 | * 540 | * ``` php 541 | * seeInCurrentUrl('home'); 544 | * // to match: /users/1 545 | * $I->seeInCurrentUrl('/users/'); 546 | * ?> 547 | * ``` 548 | * 549 | * @param $uri 550 | * Conditional Assertion: Test won't be stopped on fail 551 | * @see \Codeception\Lib\InnerBrowser::seeInCurrentUrl() 552 | */ 553 | public function canSeeInCurrentUrl($uri) { 554 | return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeInCurrentUrl', func_get_args())); 555 | } 556 | /** 557 | * [!] Method is generated. Documentation taken from corresponding module. 558 | * 559 | * Checks that current URI contains the given string. 560 | * 561 | * ``` php 562 | * seeInCurrentUrl('home'); 565 | * // to match: /users/1 566 | * $I->seeInCurrentUrl('/users/'); 567 | * ?> 568 | * ``` 569 | * 570 | * @param $uri 571 | * @see \Codeception\Lib\InnerBrowser::seeInCurrentUrl() 572 | */ 573 | public function seeInCurrentUrl($uri) { 574 | return $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeInCurrentUrl', func_get_args())); 575 | } 576 | 577 | 578 | /** 579 | * [!] Method is generated. Documentation taken from corresponding module. 580 | * 581 | * Checks that the current URI doesn't contain the given string. 582 | * 583 | * ``` php 584 | * dontSeeInCurrentUrl('/users/'); 586 | * ?> 587 | * ``` 588 | * 589 | * @param $uri 590 | * Conditional Assertion: Test won't be stopped on fail 591 | * @see \Codeception\Lib\InnerBrowser::dontSeeInCurrentUrl() 592 | */ 593 | public function cantSeeInCurrentUrl($uri) { 594 | return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeInCurrentUrl', func_get_args())); 595 | } 596 | /** 597 | * [!] Method is generated. Documentation taken from corresponding module. 598 | * 599 | * Checks that the current URI doesn't contain the given string. 600 | * 601 | * ``` php 602 | * dontSeeInCurrentUrl('/users/'); 604 | * ?> 605 | * ``` 606 | * 607 | * @param $uri 608 | * @see \Codeception\Lib\InnerBrowser::dontSeeInCurrentUrl() 609 | */ 610 | public function dontSeeInCurrentUrl($uri) { 611 | return $this->getScenario()->runStep(new \Codeception\Step\Assertion('dontSeeInCurrentUrl', func_get_args())); 612 | } 613 | 614 | 615 | /** 616 | * [!] Method is generated. Documentation taken from corresponding module. 617 | * 618 | * Checks that the current URL is equal to the given string. 619 | * Unlike `seeInCurrentUrl`, this only matches the full URL. 620 | * 621 | * ``` php 622 | * seeCurrentUrlEquals('/'); 625 | * ?> 626 | * ``` 627 | * 628 | * @param $uri 629 | * Conditional Assertion: Test won't be stopped on fail 630 | * @see \Codeception\Lib\InnerBrowser::seeCurrentUrlEquals() 631 | */ 632 | public function canSeeCurrentUrlEquals($uri) { 633 | return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeCurrentUrlEquals', func_get_args())); 634 | } 635 | /** 636 | * [!] Method is generated. Documentation taken from corresponding module. 637 | * 638 | * Checks that the current URL is equal to the given string. 639 | * Unlike `seeInCurrentUrl`, this only matches the full URL. 640 | * 641 | * ``` php 642 | * seeCurrentUrlEquals('/'); 645 | * ?> 646 | * ``` 647 | * 648 | * @param $uri 649 | * @see \Codeception\Lib\InnerBrowser::seeCurrentUrlEquals() 650 | */ 651 | public function seeCurrentUrlEquals($uri) { 652 | return $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeCurrentUrlEquals', func_get_args())); 653 | } 654 | 655 | 656 | /** 657 | * [!] Method is generated. Documentation taken from corresponding module. 658 | * 659 | * Checks that the current URL doesn't equal the given string. 660 | * Unlike `dontSeeInCurrentUrl`, this only matches the full URL. 661 | * 662 | * ``` php 663 | * dontSeeCurrentUrlEquals('/'); 666 | * ?> 667 | * ``` 668 | * 669 | * @param $uri 670 | * Conditional Assertion: Test won't be stopped on fail 671 | * @see \Codeception\Lib\InnerBrowser::dontSeeCurrentUrlEquals() 672 | */ 673 | public function cantSeeCurrentUrlEquals($uri) { 674 | return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeCurrentUrlEquals', func_get_args())); 675 | } 676 | /** 677 | * [!] Method is generated. Documentation taken from corresponding module. 678 | * 679 | * Checks that the current URL doesn't equal the given string. 680 | * Unlike `dontSeeInCurrentUrl`, this only matches the full URL. 681 | * 682 | * ``` php 683 | * dontSeeCurrentUrlEquals('/'); 686 | * ?> 687 | * ``` 688 | * 689 | * @param $uri 690 | * @see \Codeception\Lib\InnerBrowser::dontSeeCurrentUrlEquals() 691 | */ 692 | public function dontSeeCurrentUrlEquals($uri) { 693 | return $this->getScenario()->runStep(new \Codeception\Step\Assertion('dontSeeCurrentUrlEquals', func_get_args())); 694 | } 695 | 696 | 697 | /** 698 | * [!] Method is generated. Documentation taken from corresponding module. 699 | * 700 | * Checks that the current URL matches the given regular expression. 701 | * 702 | * ``` php 703 | * seeCurrentUrlMatches('~$/users/(\d+)~'); 706 | * ?> 707 | * ``` 708 | * 709 | * @param $uri 710 | * Conditional Assertion: Test won't be stopped on fail 711 | * @see \Codeception\Lib\InnerBrowser::seeCurrentUrlMatches() 712 | */ 713 | public function canSeeCurrentUrlMatches($uri) { 714 | return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeCurrentUrlMatches', func_get_args())); 715 | } 716 | /** 717 | * [!] Method is generated. Documentation taken from corresponding module. 718 | * 719 | * Checks that the current URL matches the given regular expression. 720 | * 721 | * ``` php 722 | * seeCurrentUrlMatches('~$/users/(\d+)~'); 725 | * ?> 726 | * ``` 727 | * 728 | * @param $uri 729 | * @see \Codeception\Lib\InnerBrowser::seeCurrentUrlMatches() 730 | */ 731 | public function seeCurrentUrlMatches($uri) { 732 | return $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeCurrentUrlMatches', func_get_args())); 733 | } 734 | 735 | 736 | /** 737 | * [!] Method is generated. Documentation taken from corresponding module. 738 | * 739 | * Checks that current url doesn't match the given regular expression. 740 | * 741 | * ``` php 742 | * dontSeeCurrentUrlMatches('~$/users/(\d+)~'); 745 | * ?> 746 | * ``` 747 | * 748 | * @param $uri 749 | * Conditional Assertion: Test won't be stopped on fail 750 | * @see \Codeception\Lib\InnerBrowser::dontSeeCurrentUrlMatches() 751 | */ 752 | public function cantSeeCurrentUrlMatches($uri) { 753 | return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeCurrentUrlMatches', func_get_args())); 754 | } 755 | /** 756 | * [!] Method is generated. Documentation taken from corresponding module. 757 | * 758 | * Checks that current url doesn't match the given regular expression. 759 | * 760 | * ``` php 761 | * dontSeeCurrentUrlMatches('~$/users/(\d+)~'); 764 | * ?> 765 | * ``` 766 | * 767 | * @param $uri 768 | * @see \Codeception\Lib\InnerBrowser::dontSeeCurrentUrlMatches() 769 | */ 770 | public function dontSeeCurrentUrlMatches($uri) { 771 | return $this->getScenario()->runStep(new \Codeception\Step\Assertion('dontSeeCurrentUrlMatches', func_get_args())); 772 | } 773 | 774 | 775 | /** 776 | * [!] Method is generated. Documentation taken from corresponding module. 777 | * 778 | * Executes the given regular expression against the current URI and returns the first match. 779 | * If no parameters are provided, the full URI is returned. 780 | * 781 | * ``` php 782 | * grabFromCurrentUrl('~$/user/(\d+)/~'); 784 | * $uri = $I->grabFromCurrentUrl(); 785 | * ?> 786 | * ``` 787 | * 788 | * @param null $uri 789 | * 790 | * @return mixed 791 | * @see \Codeception\Lib\InnerBrowser::grabFromCurrentUrl() 792 | */ 793 | public function grabFromCurrentUrl($uri = null) { 794 | return $this->getScenario()->runStep(new \Codeception\Step\Action('grabFromCurrentUrl', func_get_args())); 795 | } 796 | 797 | 798 | /** 799 | * [!] Method is generated. Documentation taken from corresponding module. 800 | * 801 | * Checks that the specified checkbox is checked. 802 | * 803 | * ``` php 804 | * seeCheckboxIsChecked('#agree'); // I suppose user agreed to terms 806 | * $I->seeCheckboxIsChecked('#signup_form input[type=checkbox]'); // I suppose user agreed to terms, If there is only one checkbox in form. 807 | * $I->seeCheckboxIsChecked('//form/input[@type=checkbox and @name=agree]'); 808 | * ?> 809 | * ``` 810 | * 811 | * @param $checkbox 812 | * Conditional Assertion: Test won't be stopped on fail 813 | * @see \Codeception\Lib\InnerBrowser::seeCheckboxIsChecked() 814 | */ 815 | public function canSeeCheckboxIsChecked($checkbox) { 816 | return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeCheckboxIsChecked', func_get_args())); 817 | } 818 | /** 819 | * [!] Method is generated. Documentation taken from corresponding module. 820 | * 821 | * Checks that the specified checkbox is checked. 822 | * 823 | * ``` php 824 | * seeCheckboxIsChecked('#agree'); // I suppose user agreed to terms 826 | * $I->seeCheckboxIsChecked('#signup_form input[type=checkbox]'); // I suppose user agreed to terms, If there is only one checkbox in form. 827 | * $I->seeCheckboxIsChecked('//form/input[@type=checkbox and @name=agree]'); 828 | * ?> 829 | * ``` 830 | * 831 | * @param $checkbox 832 | * @see \Codeception\Lib\InnerBrowser::seeCheckboxIsChecked() 833 | */ 834 | public function seeCheckboxIsChecked($checkbox) { 835 | return $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeCheckboxIsChecked', func_get_args())); 836 | } 837 | 838 | 839 | /** 840 | * [!] Method is generated. Documentation taken from corresponding module. 841 | * 842 | * Check that the specified checkbox is unchecked. 843 | * 844 | * ``` php 845 | * dontSeeCheckboxIsChecked('#agree'); // I suppose user didn't agree to terms 847 | * $I->seeCheckboxIsChecked('#signup_form input[type=checkbox]'); // I suppose user didn't check the first checkbox in form. 848 | * ?> 849 | * ``` 850 | * 851 | * @param $checkbox 852 | * Conditional Assertion: Test won't be stopped on fail 853 | * @see \Codeception\Lib\InnerBrowser::dontSeeCheckboxIsChecked() 854 | */ 855 | public function cantSeeCheckboxIsChecked($checkbox) { 856 | return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeCheckboxIsChecked', func_get_args())); 857 | } 858 | /** 859 | * [!] Method is generated. Documentation taken from corresponding module. 860 | * 861 | * Check that the specified checkbox is unchecked. 862 | * 863 | * ``` php 864 | * dontSeeCheckboxIsChecked('#agree'); // I suppose user didn't agree to terms 866 | * $I->seeCheckboxIsChecked('#signup_form input[type=checkbox]'); // I suppose user didn't check the first checkbox in form. 867 | * ?> 868 | * ``` 869 | * 870 | * @param $checkbox 871 | * @see \Codeception\Lib\InnerBrowser::dontSeeCheckboxIsChecked() 872 | */ 873 | public function dontSeeCheckboxIsChecked($checkbox) { 874 | return $this->getScenario()->runStep(new \Codeception\Step\Assertion('dontSeeCheckboxIsChecked', func_get_args())); 875 | } 876 | 877 | 878 | /** 879 | * [!] Method is generated. Documentation taken from corresponding module. 880 | * 881 | * Checks that the given input field or textarea contains the given value. 882 | * For fuzzy locators, fields are matched by label text, the "name" attribute, CSS, and XPath. 883 | * 884 | * ``` php 885 | * seeInField('Body','Type your comment here'); 887 | * $I->seeInField('form textarea[name=body]','Type your comment here'); 888 | * $I->seeInField('form input[type=hidden]','hidden_value'); 889 | * $I->seeInField('#searchform input','Search'); 890 | * $I->seeInField('//form/*[@name=search]','Search'); 891 | * $I->seeInField(['name' => 'search'], 'Search'); 892 | * ?> 893 | * ``` 894 | * 895 | * @param $field 896 | * @param $value 897 | * Conditional Assertion: Test won't be stopped on fail 898 | * @see \Codeception\Lib\InnerBrowser::seeInField() 899 | */ 900 | public function canSeeInField($field, $value) { 901 | return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeInField', func_get_args())); 902 | } 903 | /** 904 | * [!] Method is generated. Documentation taken from corresponding module. 905 | * 906 | * Checks that the given input field or textarea contains the given value. 907 | * For fuzzy locators, fields are matched by label text, the "name" attribute, CSS, and XPath. 908 | * 909 | * ``` php 910 | * seeInField('Body','Type your comment here'); 912 | * $I->seeInField('form textarea[name=body]','Type your comment here'); 913 | * $I->seeInField('form input[type=hidden]','hidden_value'); 914 | * $I->seeInField('#searchform input','Search'); 915 | * $I->seeInField('//form/*[@name=search]','Search'); 916 | * $I->seeInField(['name' => 'search'], 'Search'); 917 | * ?> 918 | * ``` 919 | * 920 | * @param $field 921 | * @param $value 922 | * @see \Codeception\Lib\InnerBrowser::seeInField() 923 | */ 924 | public function seeInField($field, $value) { 925 | return $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeInField', func_get_args())); 926 | } 927 | 928 | 929 | /** 930 | * [!] Method is generated. Documentation taken from corresponding module. 931 | * 932 | * Checks that an input field or textarea doesn't contain the given value. 933 | * For fuzzy locators, the field is matched by label text, CSS and XPath. 934 | * 935 | * ``` php 936 | * dontSeeInField('Body','Type your comment here'); 938 | * $I->dontSeeInField('form textarea[name=body]','Type your comment here'); 939 | * $I->dontSeeInField('form input[type=hidden]','hidden_value'); 940 | * $I->dontSeeInField('#searchform input','Search'); 941 | * $I->dontSeeInField('//form/*[@name=search]','Search'); 942 | * $I->dontSeeInField(['name' => 'search'], 'Search'); 943 | * ?> 944 | * ``` 945 | * 946 | * @param $field 947 | * @param $value 948 | * Conditional Assertion: Test won't be stopped on fail 949 | * @see \Codeception\Lib\InnerBrowser::dontSeeInField() 950 | */ 951 | public function cantSeeInField($field, $value) { 952 | return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeInField', func_get_args())); 953 | } 954 | /** 955 | * [!] Method is generated. Documentation taken from corresponding module. 956 | * 957 | * Checks that an input field or textarea doesn't contain the given value. 958 | * For fuzzy locators, the field is matched by label text, CSS and XPath. 959 | * 960 | * ``` php 961 | * dontSeeInField('Body','Type your comment here'); 963 | * $I->dontSeeInField('form textarea[name=body]','Type your comment here'); 964 | * $I->dontSeeInField('form input[type=hidden]','hidden_value'); 965 | * $I->dontSeeInField('#searchform input','Search'); 966 | * $I->dontSeeInField('//form/*[@name=search]','Search'); 967 | * $I->dontSeeInField(['name' => 'search'], 'Search'); 968 | * ?> 969 | * ``` 970 | * 971 | * @param $field 972 | * @param $value 973 | * @see \Codeception\Lib\InnerBrowser::dontSeeInField() 974 | */ 975 | public function dontSeeInField($field, $value) { 976 | return $this->getScenario()->runStep(new \Codeception\Step\Assertion('dontSeeInField', func_get_args())); 977 | } 978 | 979 | 980 | /** 981 | * [!] Method is generated. Documentation taken from corresponding module. 982 | * 983 | * Checks if the array of form parameters (name => value) are set on the form matched with the 984 | * passed selector. 985 | * 986 | * ``` php 987 | * seeInFormFields('form[name=myform]', [ 989 | * 'input1' => 'value', 990 | * 'input2' => 'other value', 991 | * ]); 992 | * ?> 993 | * ``` 994 | * 995 | * For multi-select elements, or to check values of multiple elements with the same name, an 996 | * array may be passed: 997 | * 998 | * ``` php 999 | * seeInFormFields('.form-class', [ 1001 | * 'multiselect' => [ 1002 | * 'value1', 1003 | * 'value2', 1004 | * ], 1005 | * 'checkbox[]' => [ 1006 | * 'a checked value', 1007 | * 'another checked value', 1008 | * ], 1009 | * ]); 1010 | * ?> 1011 | * ``` 1012 | * 1013 | * Additionally, checkbox values can be checked with a boolean. 1014 | * 1015 | * ``` php 1016 | * seeInFormFields('#form-id', [ 1018 | * 'checkbox1' => true, // passes if checked 1019 | * 'checkbox2' => false, // passes if unchecked 1020 | * ]); 1021 | * ?> 1022 | * ``` 1023 | * 1024 | * Pair this with submitForm for quick testing magic. 1025 | * 1026 | * ``` php 1027 | * 'value', 1030 | * 'field2' => 'another value', 1031 | * 'checkbox1' => true, 1032 | * // ... 1033 | * ]; 1034 | * $I->submitForm('//form[@id=my-form]', $form, 'submitButton'); 1035 | * // $I->amOnPage('/path/to/form-page') may be needed 1036 | * $I->seeInFormFields('//form[@id=my-form]', $form); 1037 | * ?> 1038 | * ``` 1039 | * 1040 | * @param $formSelector 1041 | * @param $params 1042 | * Conditional Assertion: Test won't be stopped on fail 1043 | * @see \Codeception\Lib\InnerBrowser::seeInFormFields() 1044 | */ 1045 | public function canSeeInFormFields($formSelector, $params) { 1046 | return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeInFormFields', func_get_args())); 1047 | } 1048 | /** 1049 | * [!] Method is generated. Documentation taken from corresponding module. 1050 | * 1051 | * Checks if the array of form parameters (name => value) are set on the form matched with the 1052 | * passed selector. 1053 | * 1054 | * ``` php 1055 | * seeInFormFields('form[name=myform]', [ 1057 | * 'input1' => 'value', 1058 | * 'input2' => 'other value', 1059 | * ]); 1060 | * ?> 1061 | * ``` 1062 | * 1063 | * For multi-select elements, or to check values of multiple elements with the same name, an 1064 | * array may be passed: 1065 | * 1066 | * ``` php 1067 | * seeInFormFields('.form-class', [ 1069 | * 'multiselect' => [ 1070 | * 'value1', 1071 | * 'value2', 1072 | * ], 1073 | * 'checkbox[]' => [ 1074 | * 'a checked value', 1075 | * 'another checked value', 1076 | * ], 1077 | * ]); 1078 | * ?> 1079 | * ``` 1080 | * 1081 | * Additionally, checkbox values can be checked with a boolean. 1082 | * 1083 | * ``` php 1084 | * seeInFormFields('#form-id', [ 1086 | * 'checkbox1' => true, // passes if checked 1087 | * 'checkbox2' => false, // passes if unchecked 1088 | * ]); 1089 | * ?> 1090 | * ``` 1091 | * 1092 | * Pair this with submitForm for quick testing magic. 1093 | * 1094 | * ``` php 1095 | * 'value', 1098 | * 'field2' => 'another value', 1099 | * 'checkbox1' => true, 1100 | * // ... 1101 | * ]; 1102 | * $I->submitForm('//form[@id=my-form]', $form, 'submitButton'); 1103 | * // $I->amOnPage('/path/to/form-page') may be needed 1104 | * $I->seeInFormFields('//form[@id=my-form]', $form); 1105 | * ?> 1106 | * ``` 1107 | * 1108 | * @param $formSelector 1109 | * @param $params 1110 | * @see \Codeception\Lib\InnerBrowser::seeInFormFields() 1111 | */ 1112 | public function seeInFormFields($formSelector, $params) { 1113 | return $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeInFormFields', func_get_args())); 1114 | } 1115 | 1116 | 1117 | /** 1118 | * [!] Method is generated. Documentation taken from corresponding module. 1119 | * 1120 | * Checks if the array of form parameters (name => value) are not set on the form matched with 1121 | * the passed selector. 1122 | * 1123 | * ``` php 1124 | * dontSeeInFormFields('form[name=myform]', [ 1126 | * 'input1' => 'non-existent value', 1127 | * 'input2' => 'other non-existent value', 1128 | * ]); 1129 | * ?> 1130 | * ``` 1131 | * 1132 | * To check that an element hasn't been assigned any one of many values, an array can be passed 1133 | * as the value: 1134 | * 1135 | * ``` php 1136 | * dontSeeInFormFields('.form-class', [ 1138 | * 'fieldName' => [ 1139 | * 'This value shouldn\'t be set', 1140 | * 'And this value shouldn\'t be set', 1141 | * ], 1142 | * ]); 1143 | * ?> 1144 | * ``` 1145 | * 1146 | * Additionally, checkbox values can be checked with a boolean. 1147 | * 1148 | * ``` php 1149 | * dontSeeInFormFields('#form-id', [ 1151 | * 'checkbox1' => true, // fails if checked 1152 | * 'checkbox2' => false, // fails if unchecked 1153 | * ]); 1154 | * ?> 1155 | * ``` 1156 | * 1157 | * @param $formSelector 1158 | * @param $params 1159 | * Conditional Assertion: Test won't be stopped on fail 1160 | * @see \Codeception\Lib\InnerBrowser::dontSeeInFormFields() 1161 | */ 1162 | public function cantSeeInFormFields($formSelector, $params) { 1163 | return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeInFormFields', func_get_args())); 1164 | } 1165 | /** 1166 | * [!] Method is generated. Documentation taken from corresponding module. 1167 | * 1168 | * Checks if the array of form parameters (name => value) are not set on the form matched with 1169 | * the passed selector. 1170 | * 1171 | * ``` php 1172 | * dontSeeInFormFields('form[name=myform]', [ 1174 | * 'input1' => 'non-existent value', 1175 | * 'input2' => 'other non-existent value', 1176 | * ]); 1177 | * ?> 1178 | * ``` 1179 | * 1180 | * To check that an element hasn't been assigned any one of many values, an array can be passed 1181 | * as the value: 1182 | * 1183 | * ``` php 1184 | * dontSeeInFormFields('.form-class', [ 1186 | * 'fieldName' => [ 1187 | * 'This value shouldn\'t be set', 1188 | * 'And this value shouldn\'t be set', 1189 | * ], 1190 | * ]); 1191 | * ?> 1192 | * ``` 1193 | * 1194 | * Additionally, checkbox values can be checked with a boolean. 1195 | * 1196 | * ``` php 1197 | * dontSeeInFormFields('#form-id', [ 1199 | * 'checkbox1' => true, // fails if checked 1200 | * 'checkbox2' => false, // fails if unchecked 1201 | * ]); 1202 | * ?> 1203 | * ``` 1204 | * 1205 | * @param $formSelector 1206 | * @param $params 1207 | * @see \Codeception\Lib\InnerBrowser::dontSeeInFormFields() 1208 | */ 1209 | public function dontSeeInFormFields($formSelector, $params) { 1210 | return $this->getScenario()->runStep(new \Codeception\Step\Assertion('dontSeeInFormFields', func_get_args())); 1211 | } 1212 | 1213 | 1214 | /** 1215 | * [!] Method is generated. Documentation taken from corresponding module. 1216 | * 1217 | * Submits the given form on the page, optionally with the given form 1218 | * values. Pass the form field's values as an array in the second 1219 | * parameter. 1220 | * 1221 | * Although this function can be used as a short-hand version of 1222 | * `fillField()`, `selectOption()`, `click()` etc. it has some important 1223 | * differences: 1224 | * 1225 | * * Only field *names* may be used, not CSS/XPath selectors nor field labels 1226 | * * If a field is sent to this function that does *not* exist on the page, 1227 | * it will silently be added to the HTTP request. This is helpful for testing 1228 | * some types of forms, but be aware that you will *not* get an exception 1229 | * like you would if you called `fillField()` or `selectOption()` with 1230 | * a missing field. 1231 | * 1232 | * Fields that are not provided will be filled by their values from the page, 1233 | * or from any previous calls to `fillField()`, `selectOption()` etc. 1234 | * You don't need to click the 'Submit' button afterwards. 1235 | * This command itself triggers the request to form's action. 1236 | * 1237 | * You can optionally specify which button's value to include 1238 | * in the request with the last parameter (as an alternative to 1239 | * explicitly setting its value in the second parameter), as 1240 | * button values are not otherwise included in the request. 1241 | * 1242 | * Examples: 1243 | * 1244 | * ``` php 1245 | * submitForm('#login', [ 1247 | * 'login' => 'davert', 1248 | * 'password' => '123456' 1249 | * ]); 1250 | * // or 1251 | * $I->submitForm('#login', [ 1252 | * 'login' => 'davert', 1253 | * 'password' => '123456' 1254 | * ], 'submitButtonName'); 1255 | * 1256 | * ``` 1257 | * 1258 | * For example, given this sample "Sign Up" form: 1259 | * 1260 | * ``` html 1261 | *
1262 | * Login: 1263 | *
1264 | * Password: 1265 | *
1266 | * Do you agree to our terms? 1267 | *
1268 | * Select pricing plan: 1269 | * 1273 | * 1274 | *
1275 | * ``` 1276 | * 1277 | * You could write the following to submit it: 1278 | * 1279 | * ``` php 1280 | * submitForm( 1282 | * '#userForm', 1283 | * [ 1284 | * 'user' => [ 1285 | * 'login' => 'Davert', 1286 | * 'password' => '123456', 1287 | * 'agree' => true 1288 | * ] 1289 | * ], 1290 | * 'submitButton' 1291 | * ); 1292 | * ``` 1293 | * Note that "2" will be the submitted value for the "plan" field, as it is 1294 | * the selected option. 1295 | * 1296 | * You can also emulate a JavaScript submission by not specifying any 1297 | * buttons in the third parameter to submitForm. 1298 | * 1299 | * ```php 1300 | * submitForm( 1302 | * '#userForm', 1303 | * [ 1304 | * 'user' => [ 1305 | * 'login' => 'Davert', 1306 | * 'password' => '123456', 1307 | * 'agree' => true 1308 | * ] 1309 | * ] 1310 | * ); 1311 | * ``` 1312 | * 1313 | * This function works well when paired with `seeInFormFields()` 1314 | * for quickly testing CRUD interfaces and form validation logic. 1315 | * 1316 | * ``` php 1317 | * 'value', 1320 | * 'field2' => 'another value', 1321 | * 'checkbox1' => true, 1322 | * // ... 1323 | * ]; 1324 | * $I->submitForm('#my-form', $form, 'submitButton'); 1325 | * // $I->amOnPage('/path/to/form-page') may be needed 1326 | * $I->seeInFormFields('#my-form', $form); 1327 | * ``` 1328 | * 1329 | * Parameter values can be set to arrays for multiple input fields 1330 | * of the same name, or multi-select combo boxes. For checkboxes, 1331 | * you can use either the string value or boolean `true`/`false` which will 1332 | * be replaced by the checkbox's value in the DOM. 1333 | * 1334 | * ``` php 1335 | * submitForm('#my-form', [ 1337 | * 'field1' => 'value', 1338 | * 'checkbox' => [ 1339 | * 'value of first checkbox', 1340 | * 'value of second checkbox', 1341 | * ], 1342 | * 'otherCheckboxes' => [ 1343 | * true, 1344 | * false, 1345 | * false 1346 | * ], 1347 | * 'multiselect' => [ 1348 | * 'first option value', 1349 | * 'second option value' 1350 | * ] 1351 | * ]); 1352 | * ``` 1353 | * 1354 | * Mixing string and boolean values for a checkbox's value is not supported 1355 | * and may produce unexpected results. 1356 | * 1357 | * Field names ending in `[]` must be passed without the trailing square 1358 | * bracket characters, and must contain an array for its value. This allows 1359 | * submitting multiple values with the same name, consider: 1360 | * 1361 | * ```php 1362 | * submitForm('#my-form', [ 1365 | * 'field[]' => 'value', 1366 | * 'field[]' => 'another value', // 'field[]' is already a defined key 1367 | * ]); 1368 | * ``` 1369 | * 1370 | * The solution is to pass an array value: 1371 | * 1372 | * ```php 1373 | * submitForm('#my-form', [ 1376 | * 'field' => [ 1377 | * 'value', 1378 | * 'another value', 1379 | * ] 1380 | * ]); 1381 | * ``` 1382 | * 1383 | * @param $selector 1384 | * @param $params 1385 | * @param $button 1386 | * @see \Codeception\Lib\InnerBrowser::submitForm() 1387 | */ 1388 | public function submitForm($selector, $params, $button = null) { 1389 | return $this->getScenario()->runStep(new \Codeception\Step\Action('submitForm', func_get_args())); 1390 | } 1391 | 1392 | 1393 | /** 1394 | * [!] Method is generated. Documentation taken from corresponding module. 1395 | * 1396 | * Fills a text field or textarea with the given string. 1397 | * 1398 | * ``` php 1399 | * fillField("//input[@type='text']", "Hello World!"); 1401 | * $I->fillField(['name' => 'email'], 'jon@mail.com'); 1402 | * ?> 1403 | * ``` 1404 | * 1405 | * @param $field 1406 | * @param $value 1407 | * @see \Codeception\Lib\InnerBrowser::fillField() 1408 | */ 1409 | public function fillField($field, $value) { 1410 | return $this->getScenario()->runStep(new \Codeception\Step\Action('fillField', func_get_args())); 1411 | } 1412 | 1413 | 1414 | /** 1415 | * [!] Method is generated. Documentation taken from corresponding module. 1416 | * 1417 | * Selects an option in a select tag or in radio button group. 1418 | * 1419 | * ``` php 1420 | * selectOption('form select[name=account]', 'Premium'); 1422 | * $I->selectOption('form input[name=payment]', 'Monthly'); 1423 | * $I->selectOption('//form/select[@name=account]', 'Monthly'); 1424 | * ?> 1425 | * ``` 1426 | * 1427 | * Provide an array for the second argument to select multiple options: 1428 | * 1429 | * ``` php 1430 | * selectOption('Which OS do you use?', array('Windows','Linux')); 1432 | * ?> 1433 | * ``` 1434 | * 1435 | * Or provide an associative array for the second argument to specifically define which selection method should be used: 1436 | * 1437 | * ``` php 1438 | * selectOption('Which OS do you use?', array('text' => 'Windows')); // Only search by text 'Windows' 1440 | * $I->selectOption('Which OS do you use?', array('value' => 'windows')); // Only search by value 'windows' 1441 | * ?> 1442 | + ``` 1443 | * 1444 | * @param $select 1445 | * @param $option 1446 | * @see \Codeception\Lib\InnerBrowser::selectOption() 1447 | */ 1448 | public function selectOption($select, $option) { 1449 | return $this->getScenario()->runStep(new \Codeception\Step\Action('selectOption', func_get_args())); 1450 | } 1451 | 1452 | 1453 | /** 1454 | * [!] Method is generated. Documentation taken from corresponding module. 1455 | * 1456 | * Ticks a checkbox. For radio buttons, use the `selectOption` method instead. 1457 | * 1458 | * ``` php 1459 | * checkOption('#agree'); 1461 | * ?> 1462 | * ``` 1463 | * 1464 | * @param $option 1465 | * @see \Codeception\Lib\InnerBrowser::checkOption() 1466 | */ 1467 | public function checkOption($option) { 1468 | return $this->getScenario()->runStep(new \Codeception\Step\Action('checkOption', func_get_args())); 1469 | } 1470 | 1471 | 1472 | /** 1473 | * [!] Method is generated. Documentation taken from corresponding module. 1474 | * 1475 | * Unticks a checkbox. 1476 | * 1477 | * ``` php 1478 | * uncheckOption('#notify'); 1480 | * ?> 1481 | * ``` 1482 | * 1483 | * @param $option 1484 | * @see \Codeception\Lib\InnerBrowser::uncheckOption() 1485 | */ 1486 | public function uncheckOption($option) { 1487 | return $this->getScenario()->runStep(new \Codeception\Step\Action('uncheckOption', func_get_args())); 1488 | } 1489 | 1490 | 1491 | /** 1492 | * [!] Method is generated. Documentation taken from corresponding module. 1493 | * 1494 | * Attaches a file relative to the Codeception data directory to the given file upload field. 1495 | * 1496 | * ``` php 1497 | * attachFile('input[@type="file"]', 'prices.xls'); 1500 | * ?> 1501 | * ``` 1502 | * 1503 | * @param $field 1504 | * @param $filename 1505 | * @see \Codeception\Lib\InnerBrowser::attachFile() 1506 | */ 1507 | public function attachFile($field, $filename) { 1508 | return $this->getScenario()->runStep(new \Codeception\Step\Action('attachFile', func_get_args())); 1509 | } 1510 | 1511 | 1512 | /** 1513 | * [!] Method is generated. Documentation taken from corresponding module. 1514 | * 1515 | * If your page triggers an ajax request, you can perform it manually. 1516 | * This action sends a GET ajax request with specified params. 1517 | * 1518 | * See ->sendAjaxPostRequest for examples. 1519 | * 1520 | * @param $uri 1521 | * @param $params 1522 | * @see \Codeception\Lib\InnerBrowser::sendAjaxGetRequest() 1523 | */ 1524 | public function sendAjaxGetRequest($uri, $params = null) { 1525 | return $this->getScenario()->runStep(new \Codeception\Step\Action('sendAjaxGetRequest', func_get_args())); 1526 | } 1527 | 1528 | 1529 | /** 1530 | * [!] Method is generated. Documentation taken from corresponding module. 1531 | * 1532 | * If your page triggers an ajax request, you can perform it manually. 1533 | * This action sends a POST ajax request with specified params. 1534 | * Additional params can be passed as array. 1535 | * 1536 | * Example: 1537 | * 1538 | * Imagine that by clicking checkbox you trigger ajax request which updates user settings. 1539 | * We emulate that click by running this ajax request manually. 1540 | * 1541 | * ``` php 1542 | * sendAjaxPostRequest('/updateSettings', array('notifications' => true)); // POST 1544 | * $I->sendAjaxGetRequest('/updateSettings', array('notifications' => true)); // GET 1545 | * 1546 | * ``` 1547 | * 1548 | * @param $uri 1549 | * @param $params 1550 | * @see \Codeception\Lib\InnerBrowser::sendAjaxPostRequest() 1551 | */ 1552 | public function sendAjaxPostRequest($uri, $params = null) { 1553 | return $this->getScenario()->runStep(new \Codeception\Step\Action('sendAjaxPostRequest', func_get_args())); 1554 | } 1555 | 1556 | 1557 | /** 1558 | * [!] Method is generated. Documentation taken from corresponding module. 1559 | * 1560 | * If your page triggers an ajax request, you can perform it manually. 1561 | * This action sends an ajax request with specified method and params. 1562 | * 1563 | * Example: 1564 | * 1565 | * You need to perform an ajax request specifying the HTTP method. 1566 | * 1567 | * ``` php 1568 | * sendAjaxRequest('PUT', '/posts/7', array('title' => 'new title')); 1570 | * 1571 | * ``` 1572 | * 1573 | * @param $method 1574 | * @param $uri 1575 | * @param $params 1576 | * @see \Codeception\Lib\InnerBrowser::sendAjaxRequest() 1577 | */ 1578 | public function sendAjaxRequest($method, $uri, $params = null) { 1579 | return $this->getScenario()->runStep(new \Codeception\Step\Action('sendAjaxRequest', func_get_args())); 1580 | } 1581 | 1582 | 1583 | /** 1584 | * [!] Method is generated. Documentation taken from corresponding module. 1585 | * 1586 | * Finds and returns the text contents of the given element. 1587 | * If a fuzzy locator is used, the element is found using CSS, XPath, 1588 | * and by matching the full page source by regular expression. 1589 | * 1590 | * ``` php 1591 | * grabTextFrom('h1'); 1593 | * $heading = $I->grabTextFrom('descendant-or-self::h1'); 1594 | * $value = $I->grabTextFrom('~ 1596 | * ``` 1597 | * 1598 | * @param $cssOrXPathOrRegex 1599 | * 1600 | * @return mixed 1601 | * @see \Codeception\Lib\InnerBrowser::grabTextFrom() 1602 | */ 1603 | public function grabTextFrom($cssOrXPathOrRegex) { 1604 | return $this->getScenario()->runStep(new \Codeception\Step\Action('grabTextFrom', func_get_args())); 1605 | } 1606 | 1607 | 1608 | /** 1609 | * [!] Method is generated. Documentation taken from corresponding module. 1610 | * 1611 | * Grabs the value of the given attribute value from the given element. 1612 | * Fails if element is not found. 1613 | * 1614 | * ``` php 1615 | * grabAttributeFrom('#tooltip', 'title'); 1617 | * ?> 1618 | * ``` 1619 | * 1620 | * 1621 | * @param $cssOrXpath 1622 | * @param $attribute 1623 | * 1624 | * @return mixed 1625 | * @see \Codeception\Lib\InnerBrowser::grabAttributeFrom() 1626 | */ 1627 | public function grabAttributeFrom($cssOrXpath, $attribute) { 1628 | return $this->getScenario()->runStep(new \Codeception\Step\Action('grabAttributeFrom', func_get_args())); 1629 | } 1630 | 1631 | 1632 | /** 1633 | * [!] Method is generated. Documentation taken from corresponding module. 1634 | * 1635 | * Grabs either the text content, or attribute values, of nodes 1636 | * matched by $cssOrXpath and returns them as an array. 1637 | * 1638 | * ```html 1639 | * First 1640 | * Second 1641 | * Third 1642 | * ``` 1643 | * 1644 | * ```php 1645 | * grabMultiple('a'); 1648 | * 1649 | * // would return ['#first', '#second', '#third'] 1650 | * $aLinks = $I->grabMultiple('a', 'href'); 1651 | * ?> 1652 | * ``` 1653 | * 1654 | * @param $cssOrXpath 1655 | * @param $attribute 1656 | * @return string[] 1657 | * @see \Codeception\Lib\InnerBrowser::grabMultiple() 1658 | */ 1659 | public function grabMultiple($cssOrXpath, $attribute = null) { 1660 | return $this->getScenario()->runStep(new \Codeception\Step\Action('grabMultiple', func_get_args())); 1661 | } 1662 | 1663 | 1664 | /** 1665 | * [!] Method is generated. Documentation taken from corresponding module. 1666 | * 1667 | * @param $field 1668 | * 1669 | * @return array|mixed|null|string 1670 | * @see \Codeception\Lib\InnerBrowser::grabValueFrom() 1671 | */ 1672 | public function grabValueFrom($field) { 1673 | return $this->getScenario()->runStep(new \Codeception\Step\Action('grabValueFrom', func_get_args())); 1674 | } 1675 | 1676 | 1677 | /** 1678 | * [!] Method is generated. Documentation taken from corresponding module. 1679 | * 1680 | * Sets a cookie with the given name and value. 1681 | * You can set additional cookie params like `domain`, `path`, `expires`, `secure` in array passed as last argument. 1682 | * 1683 | * ``` php 1684 | * setCookie('PHPSESSID', 'el4ukv0kqbvoirg7nkp4dncpk3'); 1686 | * ?> 1687 | * ``` 1688 | * 1689 | * @param $name 1690 | * @param $val 1691 | * @param array $params 1692 | * 1693 | * @return mixed 1694 | * @see \Codeception\Lib\InnerBrowser::setCookie() 1695 | */ 1696 | public function setCookie($name, $val, $params = null) { 1697 | return $this->getScenario()->runStep(new \Codeception\Step\Action('setCookie', func_get_args())); 1698 | } 1699 | 1700 | 1701 | /** 1702 | * [!] Method is generated. Documentation taken from corresponding module. 1703 | * 1704 | * Grabs a cookie value. 1705 | * You can set additional cookie params like `domain`, `path` in array passed as last argument. 1706 | * 1707 | * @param $cookie 1708 | * 1709 | * @param array $params 1710 | * @return mixed 1711 | * @see \Codeception\Lib\InnerBrowser::grabCookie() 1712 | */ 1713 | public function grabCookie($cookie, $params = null) { 1714 | return $this->getScenario()->runStep(new \Codeception\Step\Action('grabCookie', func_get_args())); 1715 | } 1716 | 1717 | 1718 | /** 1719 | * [!] Method is generated. Documentation taken from corresponding module. 1720 | * 1721 | * Checks that a cookie with the given name is set. 1722 | * You can set additional cookie params like `domain`, `path` as array passed in last argument. 1723 | * 1724 | * ``` php 1725 | * seeCookie('PHPSESSID'); 1727 | * ?> 1728 | * ``` 1729 | * 1730 | * @param $cookie 1731 | * @param array $params 1732 | * @return mixed 1733 | * Conditional Assertion: Test won't be stopped on fail 1734 | * @see \Codeception\Lib\InnerBrowser::seeCookie() 1735 | */ 1736 | public function canSeeCookie($cookie, $params = null) { 1737 | return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeCookie', func_get_args())); 1738 | } 1739 | /** 1740 | * [!] Method is generated. Documentation taken from corresponding module. 1741 | * 1742 | * Checks that a cookie with the given name is set. 1743 | * You can set additional cookie params like `domain`, `path` as array passed in last argument. 1744 | * 1745 | * ``` php 1746 | * seeCookie('PHPSESSID'); 1748 | * ?> 1749 | * ``` 1750 | * 1751 | * @param $cookie 1752 | * @param array $params 1753 | * @return mixed 1754 | * @see \Codeception\Lib\InnerBrowser::seeCookie() 1755 | */ 1756 | public function seeCookie($cookie, $params = null) { 1757 | return $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeCookie', func_get_args())); 1758 | } 1759 | 1760 | 1761 | /** 1762 | * [!] Method is generated. Documentation taken from corresponding module. 1763 | * 1764 | * Checks that there isn't a cookie with the given name. 1765 | * You can set additional cookie params like `domain`, `path` as array passed in last argument. 1766 | * 1767 | * @param $cookie 1768 | * 1769 | * @param array $params 1770 | * @return mixed 1771 | * Conditional Assertion: Test won't be stopped on fail 1772 | * @see \Codeception\Lib\InnerBrowser::dontSeeCookie() 1773 | */ 1774 | public function cantSeeCookie($cookie, $params = null) { 1775 | return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeCookie', func_get_args())); 1776 | } 1777 | /** 1778 | * [!] Method is generated. Documentation taken from corresponding module. 1779 | * 1780 | * Checks that there isn't a cookie with the given name. 1781 | * You can set additional cookie params like `domain`, `path` as array passed in last argument. 1782 | * 1783 | * @param $cookie 1784 | * 1785 | * @param array $params 1786 | * @return mixed 1787 | * @see \Codeception\Lib\InnerBrowser::dontSeeCookie() 1788 | */ 1789 | public function dontSeeCookie($cookie, $params = null) { 1790 | return $this->getScenario()->runStep(new \Codeception\Step\Assertion('dontSeeCookie', func_get_args())); 1791 | } 1792 | 1793 | 1794 | /** 1795 | * [!] Method is generated. Documentation taken from corresponding module. 1796 | * 1797 | * Unsets cookie with the given name. 1798 | * You can set additional cookie params like `domain`, `path` in array passed as last argument. 1799 | * 1800 | * @param $cookie 1801 | * 1802 | * @param array $params 1803 | * @return mixed 1804 | * @see \Codeception\Lib\InnerBrowser::resetCookie() 1805 | */ 1806 | public function resetCookie($name, $params = null) { 1807 | return $this->getScenario()->runStep(new \Codeception\Step\Action('resetCookie', func_get_args())); 1808 | } 1809 | 1810 | 1811 | /** 1812 | * [!] Method is generated. Documentation taken from corresponding module. 1813 | * 1814 | * Checks that the given element exists on the page and is visible. 1815 | * You can also specify expected attributes of this element. 1816 | * 1817 | * ``` php 1818 | * seeElement('.error'); 1820 | * $I->seeElement('//form/input[1]'); 1821 | * $I->seeElement('input', ['name' => 'login']); 1822 | * $I->seeElement('input', ['value' => '123456']); 1823 | * 1824 | * // strict locator in first arg, attributes in second 1825 | * $I->seeElement(['css' => 'form input'], ['name' => 'login']); 1826 | * ?> 1827 | * ``` 1828 | * 1829 | * @param $selector 1830 | * @param array $attributes 1831 | * @return 1832 | * Conditional Assertion: Test won't be stopped on fail 1833 | * @see \Codeception\Lib\InnerBrowser::seeElement() 1834 | */ 1835 | public function canSeeElement($selector, $attributes = null) { 1836 | return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeElement', func_get_args())); 1837 | } 1838 | /** 1839 | * [!] Method is generated. Documentation taken from corresponding module. 1840 | * 1841 | * Checks that the given element exists on the page and is visible. 1842 | * You can also specify expected attributes of this element. 1843 | * 1844 | * ``` php 1845 | * seeElement('.error'); 1847 | * $I->seeElement('//form/input[1]'); 1848 | * $I->seeElement('input', ['name' => 'login']); 1849 | * $I->seeElement('input', ['value' => '123456']); 1850 | * 1851 | * // strict locator in first arg, attributes in second 1852 | * $I->seeElement(['css' => 'form input'], ['name' => 'login']); 1853 | * ?> 1854 | * ``` 1855 | * 1856 | * @param $selector 1857 | * @param array $attributes 1858 | * @return 1859 | * @see \Codeception\Lib\InnerBrowser::seeElement() 1860 | */ 1861 | public function seeElement($selector, $attributes = null) { 1862 | return $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeElement', func_get_args())); 1863 | } 1864 | 1865 | 1866 | /** 1867 | * [!] Method is generated. Documentation taken from corresponding module. 1868 | * 1869 | * Checks that the given element is invisible or not present on the page. 1870 | * You can also specify expected attributes of this element. 1871 | * 1872 | * ``` php 1873 | * dontSeeElement('.error'); 1875 | * $I->dontSeeElement('//form/input[1]'); 1876 | * $I->dontSeeElement('input', ['name' => 'login']); 1877 | * $I->dontSeeElement('input', ['value' => '123456']); 1878 | * ?> 1879 | * ``` 1880 | * 1881 | * @param $selector 1882 | * @param array $attributes 1883 | * Conditional Assertion: Test won't be stopped on fail 1884 | * @see \Codeception\Lib\InnerBrowser::dontSeeElement() 1885 | */ 1886 | public function cantSeeElement($selector, $attributes = null) { 1887 | return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeElement', func_get_args())); 1888 | } 1889 | /** 1890 | * [!] Method is generated. Documentation taken from corresponding module. 1891 | * 1892 | * Checks that the given element is invisible or not present on the page. 1893 | * You can also specify expected attributes of this element. 1894 | * 1895 | * ``` php 1896 | * dontSeeElement('.error'); 1898 | * $I->dontSeeElement('//form/input[1]'); 1899 | * $I->dontSeeElement('input', ['name' => 'login']); 1900 | * $I->dontSeeElement('input', ['value' => '123456']); 1901 | * ?> 1902 | * ``` 1903 | * 1904 | * @param $selector 1905 | * @param array $attributes 1906 | * @see \Codeception\Lib\InnerBrowser::dontSeeElement() 1907 | */ 1908 | public function dontSeeElement($selector, $attributes = null) { 1909 | return $this->getScenario()->runStep(new \Codeception\Step\Assertion('dontSeeElement', func_get_args())); 1910 | } 1911 | 1912 | 1913 | /** 1914 | * [!] Method is generated. Documentation taken from corresponding module. 1915 | * 1916 | * Checks that there are a certain number of elements matched by the given locator on the page. 1917 | * 1918 | * ``` php 1919 | * seeNumberOfElements('tr', 10); 1921 | * $I->seeNumberOfElements('tr', [0,10]); //between 0 and 10 elements 1922 | * ?> 1923 | * ``` 1924 | * @param $selector 1925 | * @param mixed $expected : 1926 | * - string: strict number 1927 | * - array: range of numbers [0,10] 1928 | * Conditional Assertion: Test won't be stopped on fail 1929 | * @see \Codeception\Lib\InnerBrowser::seeNumberOfElements() 1930 | */ 1931 | public function canSeeNumberOfElements($selector, $expected) { 1932 | return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeNumberOfElements', func_get_args())); 1933 | } 1934 | /** 1935 | * [!] Method is generated. Documentation taken from corresponding module. 1936 | * 1937 | * Checks that there are a certain number of elements matched by the given locator on the page. 1938 | * 1939 | * ``` php 1940 | * seeNumberOfElements('tr', 10); 1942 | * $I->seeNumberOfElements('tr', [0,10]); //between 0 and 10 elements 1943 | * ?> 1944 | * ``` 1945 | * @param $selector 1946 | * @param mixed $expected : 1947 | * - string: strict number 1948 | * - array: range of numbers [0,10] 1949 | * @see \Codeception\Lib\InnerBrowser::seeNumberOfElements() 1950 | */ 1951 | public function seeNumberOfElements($selector, $expected) { 1952 | return $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeNumberOfElements', func_get_args())); 1953 | } 1954 | 1955 | 1956 | /** 1957 | * [!] Method is generated. Documentation taken from corresponding module. 1958 | * 1959 | * Checks that the given option is selected. 1960 | * 1961 | * ``` php 1962 | * seeOptionIsSelected('#form input[name=payment]', 'Visa'); 1964 | * ?> 1965 | * ``` 1966 | * 1967 | * @param $selector 1968 | * @param $optionText 1969 | * 1970 | * @return mixed 1971 | * Conditional Assertion: Test won't be stopped on fail 1972 | * @see \Codeception\Lib\InnerBrowser::seeOptionIsSelected() 1973 | */ 1974 | public function canSeeOptionIsSelected($selector, $optionText) { 1975 | return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeOptionIsSelected', func_get_args())); 1976 | } 1977 | /** 1978 | * [!] Method is generated. Documentation taken from corresponding module. 1979 | * 1980 | * Checks that the given option is selected. 1981 | * 1982 | * ``` php 1983 | * seeOptionIsSelected('#form input[name=payment]', 'Visa'); 1985 | * ?> 1986 | * ``` 1987 | * 1988 | * @param $selector 1989 | * @param $optionText 1990 | * 1991 | * @return mixed 1992 | * @see \Codeception\Lib\InnerBrowser::seeOptionIsSelected() 1993 | */ 1994 | public function seeOptionIsSelected($selector, $optionText) { 1995 | return $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeOptionIsSelected', func_get_args())); 1996 | } 1997 | 1998 | 1999 | /** 2000 | * [!] Method is generated. Documentation taken from corresponding module. 2001 | * 2002 | * Checks that the given option is not selected. 2003 | * 2004 | * ``` php 2005 | * dontSeeOptionIsSelected('#form input[name=payment]', 'Visa'); 2007 | * ?> 2008 | * ``` 2009 | * 2010 | * @param $selector 2011 | * @param $optionText 2012 | * 2013 | * @return mixed 2014 | * Conditional Assertion: Test won't be stopped on fail 2015 | * @see \Codeception\Lib\InnerBrowser::dontSeeOptionIsSelected() 2016 | */ 2017 | public function cantSeeOptionIsSelected($selector, $optionText) { 2018 | return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeOptionIsSelected', func_get_args())); 2019 | } 2020 | /** 2021 | * [!] Method is generated. Documentation taken from corresponding module. 2022 | * 2023 | * Checks that the given option is not selected. 2024 | * 2025 | * ``` php 2026 | * dontSeeOptionIsSelected('#form input[name=payment]', 'Visa'); 2028 | * ?> 2029 | * ``` 2030 | * 2031 | * @param $selector 2032 | * @param $optionText 2033 | * 2034 | * @return mixed 2035 | * @see \Codeception\Lib\InnerBrowser::dontSeeOptionIsSelected() 2036 | */ 2037 | public function dontSeeOptionIsSelected($selector, $optionText) { 2038 | return $this->getScenario()->runStep(new \Codeception\Step\Assertion('dontSeeOptionIsSelected', func_get_args())); 2039 | } 2040 | 2041 | 2042 | /** 2043 | * [!] Method is generated. Documentation taken from corresponding module. 2044 | * 2045 | * Asserts that current page has 404 response status code. 2046 | * Conditional Assertion: Test won't be stopped on fail 2047 | * @see \Codeception\Lib\InnerBrowser::seePageNotFound() 2048 | */ 2049 | public function canSeePageNotFound() { 2050 | return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seePageNotFound', func_get_args())); 2051 | } 2052 | /** 2053 | * [!] Method is generated. Documentation taken from corresponding module. 2054 | * 2055 | * Asserts that current page has 404 response status code. 2056 | * @see \Codeception\Lib\InnerBrowser::seePageNotFound() 2057 | */ 2058 | public function seePageNotFound() { 2059 | return $this->getScenario()->runStep(new \Codeception\Step\Assertion('seePageNotFound', func_get_args())); 2060 | } 2061 | 2062 | 2063 | /** 2064 | * [!] Method is generated. Documentation taken from corresponding module. 2065 | * 2066 | * Checks that response code is equal to value provided. 2067 | * 2068 | * ```php 2069 | * seeResponseCodeIs(200); 2071 | * 2072 | * // recommended \Codeception\Util\HttpCode 2073 | * $I->seeResponseCodeIs(\Codeception\Util\HttpCode::OK); 2074 | * ``` 2075 | * 2076 | * @param $code 2077 | * Conditional Assertion: Test won't be stopped on fail 2078 | * @see \Codeception\Lib\InnerBrowser::seeResponseCodeIs() 2079 | */ 2080 | public function canSeeResponseCodeIs($code) { 2081 | return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeResponseCodeIs', func_get_args())); 2082 | } 2083 | /** 2084 | * [!] Method is generated. Documentation taken from corresponding module. 2085 | * 2086 | * Checks that response code is equal to value provided. 2087 | * 2088 | * ```php 2089 | * seeResponseCodeIs(200); 2091 | * 2092 | * // recommended \Codeception\Util\HttpCode 2093 | * $I->seeResponseCodeIs(\Codeception\Util\HttpCode::OK); 2094 | * ``` 2095 | * 2096 | * @param $code 2097 | * @see \Codeception\Lib\InnerBrowser::seeResponseCodeIs() 2098 | */ 2099 | public function seeResponseCodeIs($code) { 2100 | return $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeResponseCodeIs', func_get_args())); 2101 | } 2102 | 2103 | 2104 | /** 2105 | * [!] Method is generated. Documentation taken from corresponding module. 2106 | * 2107 | * Checks that response code is equal to value provided. 2108 | * 2109 | * ```php 2110 | * dontSeeResponseCodeIs(200); 2112 | * 2113 | * // recommended \Codeception\Util\HttpCode 2114 | * $I->dontSeeResponseCodeIs(\Codeception\Util\HttpCode::OK); 2115 | * ``` 2116 | * @param $code 2117 | * Conditional Assertion: Test won't be stopped on fail 2118 | * @see \Codeception\Lib\InnerBrowser::dontSeeResponseCodeIs() 2119 | */ 2120 | public function cantSeeResponseCodeIs($code) { 2121 | return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeResponseCodeIs', func_get_args())); 2122 | } 2123 | /** 2124 | * [!] Method is generated. Documentation taken from corresponding module. 2125 | * 2126 | * Checks that response code is equal to value provided. 2127 | * 2128 | * ```php 2129 | * dontSeeResponseCodeIs(200); 2131 | * 2132 | * // recommended \Codeception\Util\HttpCode 2133 | * $I->dontSeeResponseCodeIs(\Codeception\Util\HttpCode::OK); 2134 | * ``` 2135 | * @param $code 2136 | * @see \Codeception\Lib\InnerBrowser::dontSeeResponseCodeIs() 2137 | */ 2138 | public function dontSeeResponseCodeIs($code) { 2139 | return $this->getScenario()->runStep(new \Codeception\Step\Assertion('dontSeeResponseCodeIs', func_get_args())); 2140 | } 2141 | 2142 | 2143 | /** 2144 | * [!] Method is generated. Documentation taken from corresponding module. 2145 | * 2146 | * Checks that the page title contains the given string. 2147 | * 2148 | * ``` php 2149 | * seeInTitle('Blog - Post #1'); 2151 | * ?> 2152 | * ``` 2153 | * 2154 | * @param $title 2155 | * 2156 | * @return mixed 2157 | * Conditional Assertion: Test won't be stopped on fail 2158 | * @see \Codeception\Lib\InnerBrowser::seeInTitle() 2159 | */ 2160 | public function canSeeInTitle($title) { 2161 | return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('seeInTitle', func_get_args())); 2162 | } 2163 | /** 2164 | * [!] Method is generated. Documentation taken from corresponding module. 2165 | * 2166 | * Checks that the page title contains the given string. 2167 | * 2168 | * ``` php 2169 | * seeInTitle('Blog - Post #1'); 2171 | * ?> 2172 | * ``` 2173 | * 2174 | * @param $title 2175 | * 2176 | * @return mixed 2177 | * @see \Codeception\Lib\InnerBrowser::seeInTitle() 2178 | */ 2179 | public function seeInTitle($title) { 2180 | return $this->getScenario()->runStep(new \Codeception\Step\Assertion('seeInTitle', func_get_args())); 2181 | } 2182 | 2183 | 2184 | /** 2185 | * [!] Method is generated. Documentation taken from corresponding module. 2186 | * 2187 | * Checks that the page title does not contain the given string. 2188 | * 2189 | * @param $title 2190 | * 2191 | * @return mixed 2192 | * Conditional Assertion: Test won't be stopped on fail 2193 | * @see \Codeception\Lib\InnerBrowser::dontSeeInTitle() 2194 | */ 2195 | public function cantSeeInTitle($title) { 2196 | return $this->getScenario()->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeInTitle', func_get_args())); 2197 | } 2198 | /** 2199 | * [!] Method is generated. Documentation taken from corresponding module. 2200 | * 2201 | * Checks that the page title does not contain the given string. 2202 | * 2203 | * @param $title 2204 | * 2205 | * @return mixed 2206 | * @see \Codeception\Lib\InnerBrowser::dontSeeInTitle() 2207 | */ 2208 | public function dontSeeInTitle($title) { 2209 | return $this->getScenario()->runStep(new \Codeception\Step\Assertion('dontSeeInTitle', func_get_args())); 2210 | } 2211 | 2212 | 2213 | /** 2214 | * [!] Method is generated. Documentation taken from corresponding module. 2215 | * 2216 | * Switch to iframe or frame on the page. 2217 | * 2218 | * Example: 2219 | * ``` html 2220 | *