├── .ddev └── config.yaml ├── .gitignore ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── composer.json ├── phpcs.xml.dist ├── phpunit-integration.xml.dist ├── phpunit.xml.dist ├── src ├── Exception │ ├── RuntimeException.php │ └── Throwable.php ├── Helper │ ├── DbUrlParser.php │ └── SaltGenerator.php └── WpTestsStarter.php └── tests ├── Unit ├── Helper │ ├── DbUrlParserTest.php │ └── SaltGeneratorTest.php └── WpTestsStarterTest.php ├── WpIntegration ├── WpTestsStarterDefaultConstantsTest.php └── WpTestsStarterTest.php ├── bootstrap.dist.php ├── plugins └── test-plugin.php └── tmp └── README.md /.ddev/config.yaml: -------------------------------------------------------------------------------- 1 | name: wp-tests-starter 2 | type: php 3 | docroot: public 4 | php_version: "7.4" 5 | webserver_type: nginx-fpm 6 | router_http_port: "80" 7 | router_https_port: "443" 8 | xdebug_enabled: true 9 | additional_hostnames: [] 10 | additional_fqdns: [] 11 | database: 12 | type: mariadb 13 | version: "10.4" 14 | nfs_mount_enabled: false 15 | mutagen_enabled: false 16 | project_tld: localhost 17 | use_dns_when_possible: true 18 | composer_version: "2" 19 | web_environment: [] 20 | nodejs_version: "16" 21 | 22 | # Key features of ddev's config.yaml: 23 | 24 | # name: # Name of the project, automatically provides 25 | # http://projectname.ddev.site and https://projectname.ddev.site 26 | 27 | # type: # drupal6/7/8, backdrop, typo3, wordpress, php 28 | 29 | # docroot: # Relative path to the directory containing index.php. 30 | 31 | # php_version: "7.4" # PHP version to use, "5.6", "7.0", "7.1", "7.2", "7.3", "7.4", "8.0", "8.1", "8.2" 32 | 33 | # You can explicitly specify the webimage but this 34 | # is not recommended, as the images are often closely tied to ddev's' behavior, 35 | # so this can break upgrades. 36 | 37 | # webimage: # nginx/php docker image. 38 | 39 | # database: 40 | # type: # mysql, mariadb 41 | # version: # database version, like "10.3" or "8.0" 42 | # Note that mariadb_version or mysql_version from v1.18 and earlier 43 | # will automatically be converted to this notation with just a "ddev config --auto" 44 | 45 | # router_http_port: # Port to be used for http (defaults to port 80) 46 | # router_https_port: # Port for https (defaults to 443) 47 | 48 | # xdebug_enabled: false # Set to true to enable xdebug and "ddev start" or "ddev restart" 49 | # Note that for most people the commands 50 | # "ddev xdebug" to enable xdebug and "ddev xdebug off" to disable it work better, 51 | # as leaving xdebug enabled all the time is a big performance hit. 52 | 53 | # xhprof_enabled: false # Set to true to enable xhprof and "ddev start" or "ddev restart" 54 | # Note that for most people the commands 55 | # "ddev xhprof" to enable xhprof and "ddev xhprof off" to disable it work better, 56 | # as leaving xhprof enabled all the time is a big performance hit. 57 | 58 | # webserver_type: nginx-fpm # or apache-fpm 59 | 60 | # timezone: Europe/Berlin 61 | # This is the timezone used in the containers and by PHP; 62 | # it can be set to any valid timezone, 63 | # see https://en.wikipedia.org/wiki/List_of_tz_database_time_zones 64 | # For example Europe/Dublin or MST7MDT 65 | 66 | # composer_root: 67 | # Relative path to the composer root directory from the project root. This is 68 | # the directory which contains the composer.json and where all Composer related 69 | # commands are executed. 70 | 71 | # composer_version: "2" 72 | # You can set it to "" or "2" (default) for Composer v2 or "1" for Composer v1 73 | # to use the latest major version available at the time your container is built. 74 | # It is also possible to use each other Composer version channel. This includes: 75 | # - 2.2 (latest Composer LTS version) 76 | # - stable 77 | # - preview 78 | # - snapshot 79 | # Alternatively, an explicit Composer version may be specified, for example "2.2.18". 80 | # To reinstall Composer after the image was built, run "ddev debug refresh". 81 | 82 | # nodejs_version: "16" 83 | # change from the default system Node.js version to another supported version, like 12, 14, 17, 18. 84 | # Note that you can use 'ddev nvm' or nvm inside the web container to provide nearly any 85 | # Node.js version, including v6, etc. 86 | 87 | # additional_hostnames: 88 | # - somename 89 | # - someothername 90 | # would provide http and https URLs for "somename.ddev.site" 91 | # and "someothername.ddev.site". 92 | 93 | # additional_fqdns: 94 | # - example.com 95 | # - sub1.example.com 96 | # would provide http and https URLs for "example.com" and "sub1.example.com" 97 | # Please take care with this because it can cause great confusion. 98 | 99 | # upload_dir: custom/upload/dir 100 | # would set the destination path for ddev import-files to /custom/upload/dir 101 | # When mutagen is enabled this path is bind-mounted so that all the files 102 | # in the upload_dir don't have to be synced into mutagen 103 | 104 | # working_dir: 105 | # web: /var/www/html 106 | # db: /home 107 | # would set the default working directory for the web and db services. 108 | # These values specify the destination directory for ddev ssh and the 109 | # directory in which commands passed into ddev exec are run. 110 | 111 | # omit_containers: [db, dba, ddev-ssh-agent] 112 | # Currently only these containers are supported. Some containers can also be 113 | # omitted globally in the ~/.ddev/global_config.yaml. Note that if you omit 114 | # the "db" container, several standard features of ddev that access the 115 | # database container will be unusable. In the global configuration it is also 116 | # possible to omit ddev-router, but not here. 117 | 118 | # nfs_mount_enabled: false 119 | # Great performance improvement but requires host configuration first. 120 | # See https://ddev.readthedocs.io/en/latest/users/install/performance/#nfs 121 | 122 | # mutagen_enabled: false 123 | # Performance improvement using mutagen asynchronous updates. 124 | # See https://ddev.readthedocs.io/en/latest/users/install/performance/#mutagen 125 | 126 | # fail_on_hook_fail: False 127 | # Decide whether 'ddev start' should be interrupted by a failing hook 128 | 129 | # host_https_port: "59002" 130 | # The host port binding for https can be explicitly specified. It is 131 | # dynamic unless otherwise specified. 132 | # This is not used by most people, most people use the *router* instead 133 | # of the localhost port. 134 | 135 | # host_webserver_port: "59001" 136 | # The host port binding for the ddev-webserver can be explicitly specified. It is 137 | # dynamic unless otherwise specified. 138 | # This is not used by most people, most people use the *router* instead 139 | # of the localhost port. 140 | 141 | # host_db_port: "59002" 142 | # The host port binding for the ddev-dbserver can be explicitly specified. It is dynamic 143 | # unless explicitly specified. 144 | 145 | # phpmyadmin_port: "8036" 146 | # phpmyadmin_https_port: "8037" 147 | # The PHPMyAdmin ports can be changed from the default 8036 and 8037 148 | 149 | # host_phpmyadmin_port: "8036" 150 | # The phpmyadmin (dba) port is not normally bound on the host at all, instead being routed 151 | # through ddev-router, but it can be specified and bound. 152 | 153 | # mailhog_port: "8025" 154 | # mailhog_https_port: "8026" 155 | # The MailHog ports can be changed from the default 8025 and 8026 156 | 157 | # host_mailhog_port: "8025" 158 | # The mailhog port is not normally bound on the host at all, instead being routed 159 | # through ddev-router, but it can be bound directly to localhost if specified here. 160 | 161 | # webimage_extra_packages: [php7.4-tidy, php-bcmath] 162 | # Extra Debian packages that are needed in the webimage can be added here 163 | 164 | # dbimage_extra_packages: [telnet,netcat] 165 | # Extra Debian packages that are needed in the dbimage can be added here 166 | 167 | # use_dns_when_possible: true 168 | # If the host has internet access and the domain configured can 169 | # successfully be looked up, DNS will be used for hostname resolution 170 | # instead of editing /etc/hosts 171 | # Defaults to true 172 | 173 | # project_tld: ddev.site 174 | # The top-level domain used for project URLs 175 | # The default "ddev.site" allows DNS lookup via a wildcard 176 | # If you prefer you can change this to "ddev.local" to preserve 177 | # pre-v1.9 behavior. 178 | 179 | # ngrok_args: --basic-auth username:pass1234 180 | # Provide extra flags to the "ngrok http" command, see 181 | # https://ngrok.com/docs#http or run "ngrok http -h" 182 | 183 | # disable_settings_management: false 184 | # If true, ddev will not create CMS-specific settings files like 185 | # Drupal's settings.php/settings.ddev.php or TYPO3's AdditionalConfiguration.php 186 | # In this case the user must provide all such settings. 187 | 188 | # You can inject environment variables into the web container with: 189 | # web_environment: 190 | # - SOMEENV=somevalue 191 | # - SOMEOTHERENV=someothervalue 192 | 193 | # no_project_mount: false 194 | # (Experimental) If true, ddev will not mount the project into the web container; 195 | # the user is responsible for mounting it manually or via a script. 196 | # This is to enable experimentation with alternate file mounting strategies. 197 | # For advanced users only! 198 | 199 | # bind_all_interfaces: false 200 | # If true, host ports will be bound on all network interfaces, 201 | # not just the localhost interface. This means that ports 202 | # will be available on the local network if the host firewall 203 | # allows it. 204 | 205 | # default_container_timeout: 120 206 | # The default time that ddev waits for all containers to become ready can be increased from 207 | # the default 120. This helps in importing huge databases, for example. 208 | 209 | #web_extra_exposed_ports: 210 | #- name: nodejs 211 | # container_port: 3000 212 | # http_port: 2999 213 | # https_port: 3000 214 | #- name: something 215 | # container_port: 4000 216 | # https_port: 4000 217 | # http_port: 3999 218 | # Allows a set of extra ports to be exposed via ddev-router 219 | # The port behavior on the ddev-webserver must be arranged separately, for example 220 | # using web_extra_daemons. 221 | # For example, with a web app on port 3000 inside the container, this config would 222 | # expose that web app on https://.ddev.site:9999 and http://.ddev.site:9998 223 | # web_extra_exposed_ports: 224 | # - container_port: 3000 225 | # http_port: 9998 226 | # https_port: 9999 227 | 228 | #web_extra_daemons: 229 | #- name: "http-1" 230 | # command: "/var/www/html/node_modules/.bin/http-server -p 3000" 231 | # directory: /var/www/html 232 | #- name: "http-2" 233 | # command: "/var/www/html/node_modules/.bin/http-server /var/www/html/sub -p 3000" 234 | # directory: /var/www/html 235 | 236 | # override_config: false 237 | # By default, config.*.yaml files are *merged* into the configuration 238 | # But this means that some things can't be overridden 239 | # For example, if you have 'nfs_mount_enabled: true'' you can't override it with a merge 240 | # and you can't erase existing hooks or all environment variables. 241 | # However, with "override_config: true" in a particular config.*.yaml file, 242 | # 'nfs_mount_enabled: false' can override the existing values, and 243 | # hooks: 244 | # post-start: [] 245 | # or 246 | # web_environment: [] 247 | # or 248 | # additional_hostnames: [] 249 | # can have their intended affect. 'override_config' affects only behavior of the 250 | # config.*.yaml file it exists in. 251 | 252 | # Many ddev commands can be extended to run tasks before or after the 253 | # ddev command is executed, for example "post-start", "post-import-db", 254 | # "pre-composer", "post-composer" 255 | # See https://ddev.readthedocs.io/en/stable/users/extend/custom-commands/ for more 256 | # information on the commands that can be extended and the tasks you can define 257 | # for them. Example: 258 | #hooks: 259 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .ddev/*.local.yaml 2 | vendor/ 3 | tmp/ 4 | composer.lock 5 | phpunit-integration.xml 6 | phpunit.xml 7 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog of inpsyde/wp-tests-starter 2 | 3 | ## main 4 | * Introduces Changelog 5 | * Use new default branch `main` 6 | * Raise minimum PHP version to 7.4 7 | * Behavioral changes 8 | * `WpTestsStarter::useConst()` does not define the constant immediately like `::defineConst()` did 9 | * API changes 10 | * Remove `Common\SaltGeneratorInterface` 11 | * Rename `Common\*` to `Helper\*` 12 | * Introduce `Helper\DbUrlParser` 13 | * Rename mutator methods `WpTestsStarter::set*()` and `::define*()` to `::use*()` 14 | * Rename method `WpTestsStarter::defineConst()` to `::useConst()` 15 | * Add method `WpTestsStarter::addLivePlugin()` 16 | * Add methods `WpTestsStarter::addFilter()` and `::addAction()` to mimic `add_filter()` and `add_action()` before WP is loaded 17 | * Internal refactoring 18 | * Move phpunit*.xml.dist to root directory 19 | * Reformat code 20 | * Add DDEV as development environment 21 | * Update dev dependencies 22 | 23 | ## 1.0.2 24 | * Add `WpTestsStarter::getConfigFile()` 25 | * Fix: write constants into test config file #3 26 | 27 | ## 1.0.1 28 | * typo fixes 29 | * License added 30 | 31 | ## 1.0.0 32 | * initial release 33 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Thanks for contributing to WP Tests Starter 2 | 3 | # Getting Started 4 | * Make sure you have a [GitHub account](https://github.com/signup/free). 5 | * See if your issue has been discussed (or even fixed) earlier. You can [search for existing issues](https://github.com/inpsyde/WP-Tests-Starter/issues?utf8=%E2%9C%93&q=is%3Aissue). 6 | * Assuming it does not already exist, [create a new issue](https://github.com/inpsyde/WP-Tests-Starter/issues/new). 7 | * Clearly describe the issue including steps to reproduce when it is a bug. 8 | * Make sure you fill in the earliest version that you know has the issue. 9 | * Fork the repository on GitHub. 10 | 11 | # Making Changes 12 | * Create a topic branch from where you want to start your work. 13 | * This is usually the `master` branch. 14 | * To quickly create a topic branch based on the `master` branch: 15 | * `git checkout -b issue/%YOUR-ISSUE-NUMBER%_%DESCRIPTIVE-TITLE% master` 16 | * a good example is `issue/118_html_lang_attribute` 17 | * Please avoid working directly on the `master` branch. 18 | * Make commits of logical units. 19 | * Make sure your commit messages are descriptive. 20 | 21 | # Submitting Changes 22 | * Push your changes to the according topic branch in your fork of the repository. 23 | * [Create a pull request](https://github.com/inpsyde/WP-Tests-Starter/compare) to our repository. 24 | * Wait for feedback. We look at pull requests on a regular basis. 25 | 26 | # License 27 | By contributing code to _WP Tests Starter_, you grant its use under this [License](LICENSE). 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | ### GNU GENERAL PUBLIC LICENSE 2 | 3 | Version 2, June 1991 4 | 5 | Copyright (C) 1989, 1991 Free Software Foundation, Inc. 6 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA 7 | 8 | Everyone is permitted to copy and distribute verbatim copies 9 | of this license document, but changing it is not allowed. 10 | 11 | ### Preamble 12 | 13 | The licenses for most software are designed to take away your freedom 14 | to share and change it. By contrast, the GNU General Public License is 15 | intended to guarantee your freedom to share and change free 16 | software--to make sure the software is free for all its users. This 17 | General Public License applies to most of the Free Software 18 | Foundation's software and to any other program whose authors commit to 19 | using it. (Some other Free Software Foundation software is covered by 20 | the GNU Lesser General Public License instead.) You can apply it to 21 | your programs, too. 22 | 23 | When we speak of free software, we are referring to freedom, not 24 | price. Our General Public Licenses are designed to make sure that you 25 | have the freedom to distribute copies of free software (and charge for 26 | this service if you wish), that you receive source code or can get it 27 | if you want it, that you can change the software or use pieces of it 28 | in new free programs; and that you know you can do these things. 29 | 30 | To protect your rights, we need to make restrictions that forbid 31 | anyone to deny you these rights or to ask you to surrender the rights. 32 | These restrictions translate to certain responsibilities for you if 33 | you distribute copies of the software, or if you modify it. 34 | 35 | For example, if you distribute copies of such a program, whether 36 | gratis or for a fee, you must give the recipients all the rights that 37 | you have. You must make sure that they, too, receive or can get the 38 | source code. And you must show them these terms so they know their 39 | rights. 40 | 41 | We protect your rights with two steps: (1) copyright the software, and 42 | (2) offer you this license which gives you legal permission to copy, 43 | distribute and/or modify the software. 44 | 45 | Also, for each author's protection and ours, we want to make certain 46 | that everyone understands that there is no warranty for this free 47 | software. If the software is modified by someone else and passed on, 48 | we want its recipients to know that what they have is not the 49 | original, so that any problems introduced by others will not reflect 50 | on the original authors' reputations. 51 | 52 | Finally, any free program is threatened constantly by software 53 | patents. We wish to avoid the danger that redistributors of a free 54 | program will individually obtain patent licenses, in effect making the 55 | program proprietary. To prevent this, we have made it clear that any 56 | patent must be licensed for everyone's free use or not licensed at 57 | all. 58 | 59 | The precise terms and conditions for copying, distribution and 60 | modification follow. 61 | 62 | ### TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 63 | 64 | **0.** This License applies to any program or other work which 65 | contains a notice placed by the copyright holder saying it may be 66 | distributed under the terms of this General Public License. The 67 | "Program", below, refers to any such program or work, and a "work 68 | based on the Program" means either the Program or any derivative work 69 | under copyright law: that is to say, a work containing the Program or 70 | a portion of it, either verbatim or with modifications and/or 71 | translated into another language. (Hereinafter, translation is 72 | included without limitation in the term "modification".) Each licensee 73 | is addressed as "you". 74 | 75 | Activities other than copying, distribution and modification are not 76 | covered by this License; they are outside its scope. The act of 77 | running the Program is not restricted, and the output from the Program 78 | is covered only if its contents constitute a work based on the Program 79 | (independent of having been made by running the Program). Whether that 80 | is true depends on what the Program does. 81 | 82 | **1.** You may copy and distribute verbatim copies of the Program's 83 | source code as you receive it, in any medium, provided that you 84 | conspicuously and appropriately publish on each copy an appropriate 85 | copyright notice and disclaimer of warranty; keep intact all the 86 | notices that refer to this License and to the absence of any warranty; 87 | and give any other recipients of the Program a copy of this License 88 | along with the Program. 89 | 90 | You may charge a fee for the physical act of transferring a copy, and 91 | you may at your option offer warranty protection in exchange for a 92 | fee. 93 | 94 | **2.** You may modify your copy or copies of the Program or any 95 | portion of it, thus forming a work based on the Program, and copy and 96 | distribute such modifications or work under the terms of Section 1 97 | above, provided that you also meet all of these conditions: 98 | 99 | 100 | **a)** You must cause the modified files to carry prominent notices 101 | stating that you changed the files and the date of any change. 102 | 103 | 104 | **b)** You must cause any work that you distribute or publish, that in 105 | whole or in part contains or is derived from the Program or any part 106 | thereof, to be licensed as a whole at no charge to all third parties 107 | under the terms of this License. 108 | 109 | 110 | **c)** If the modified program normally reads commands interactively 111 | when run, you must cause it, when started running for such interactive 112 | use in the most ordinary way, to print or display an announcement 113 | including an appropriate copyright notice and a notice that there is 114 | no warranty (or else, saying that you provide a warranty) and that 115 | users may redistribute the program under these conditions, and telling 116 | the user how to view a copy of this License. (Exception: if the 117 | Program itself is interactive but does not normally print such an 118 | announcement, your work based on the Program is not required to print 119 | an announcement.) 120 | 121 | These requirements apply to the modified work as a whole. If 122 | identifiable sections of that work are not derived from the Program, 123 | and can be reasonably considered independent and separate works in 124 | themselves, then this License, and its terms, do not apply to those 125 | sections when you distribute them as separate works. But when you 126 | distribute the same sections as part of a whole which is a work based 127 | on the Program, the distribution of the whole must be on the terms of 128 | this License, whose permissions for other licensees extend to the 129 | entire whole, and thus to each and every part regardless of who wrote 130 | it. 131 | 132 | Thus, it is not the intent of this section to claim rights or contest 133 | your rights to work written entirely by you; rather, the intent is to 134 | exercise the right to control the distribution of derivative or 135 | collective works based on the Program. 136 | 137 | In addition, mere aggregation of another work not based on the Program 138 | with the Program (or with a work based on the Program) on a volume of 139 | a storage or distribution medium does not bring the other work under 140 | the scope of this License. 141 | 142 | **3.** You may copy and distribute the Program (or a work based on it, 143 | under Section 2) in object code or executable form under the terms of 144 | Sections 1 and 2 above provided that you also do one of the following: 145 | 146 | 147 | **a)** Accompany it with the complete corresponding machine-readable 148 | source code, which must be distributed under the terms of Sections 1 149 | and 2 above on a medium customarily used for software interchange; or, 150 | 151 | 152 | **b)** Accompany it with a written offer, valid for at least three 153 | years, to give any third party, for a charge no more than your cost of 154 | physically performing source distribution, a complete machine-readable 155 | copy of the corresponding source code, to be distributed under the 156 | terms of Sections 1 and 2 above on a medium customarily used for 157 | software interchange; or, 158 | 159 | 160 | **c)** Accompany it with the information you received as to the offer 161 | to distribute corresponding source code. (This alternative is allowed 162 | only for noncommercial distribution and only if you received the 163 | program in object code or executable form with such an offer, in 164 | accord with Subsection b above.) 165 | 166 | The source code for a work means the preferred form of the work for 167 | making modifications to it. For an executable work, complete source 168 | code means all the source code for all modules it contains, plus any 169 | associated interface definition files, plus the scripts used to 170 | control compilation and installation of the executable. However, as a 171 | special exception, the source code distributed need not include 172 | anything that is normally distributed (in either source or binary 173 | form) with the major components (compiler, kernel, and so on) of the 174 | operating system on which the executable runs, unless that component 175 | itself accompanies the executable. 176 | 177 | If distribution of executable or object code is made by offering 178 | access to copy from a designated place, then offering equivalent 179 | access to copy the source code from the same place counts as 180 | distribution of the source code, even though third parties are not 181 | compelled to copy the source along with the object code. 182 | 183 | **4.** You may not copy, modify, sublicense, or distribute the Program 184 | except as expressly provided under this License. Any attempt otherwise 185 | to copy, modify, sublicense or distribute the Program is void, and 186 | will automatically terminate your rights under this License. However, 187 | parties who have received copies, or rights, from you under this 188 | License will not have their licenses terminated so long as such 189 | parties remain in full compliance. 190 | 191 | **5.** You are not required to accept this License, since you have not 192 | signed it. However, nothing else grants you permission to modify or 193 | distribute the Program or its derivative works. These actions are 194 | prohibited by law if you do not accept this License. Therefore, by 195 | modifying or distributing the Program (or any work based on the 196 | Program), you indicate your acceptance of this License to do so, and 197 | all its terms and conditions for copying, distributing or modifying 198 | the Program or works based on it. 199 | 200 | **6.** Each time you redistribute the Program (or any work based on 201 | the Program), the recipient automatically receives a license from the 202 | original licensor to copy, distribute or modify the Program subject to 203 | these terms and conditions. You may not impose any further 204 | restrictions on the recipients' exercise of the rights granted herein. 205 | You are not responsible for enforcing compliance by third parties to 206 | this License. 207 | 208 | **7.** If, as a consequence of a court judgment or allegation of 209 | patent infringement or for any other reason (not limited to patent 210 | issues), conditions are imposed on you (whether by court order, 211 | agreement or otherwise) that contradict the conditions of this 212 | License, they do not excuse you from the conditions of this License. 213 | If you cannot distribute so as to satisfy simultaneously your 214 | obligations under this License and any other pertinent obligations, 215 | then as a consequence you may not distribute the Program at all. For 216 | example, if a patent license would not permit royalty-free 217 | redistribution of the Program by all those who receive copies directly 218 | or indirectly through you, then the only way you could satisfy both it 219 | and this License would be to refrain entirely from distribution of the 220 | Program. 221 | 222 | If any portion of this section is held invalid or unenforceable under 223 | any particular circumstance, the balance of the section is intended to 224 | apply and the section as a whole is intended to apply in other 225 | circumstances. 226 | 227 | It is not the purpose of this section to induce you to infringe any 228 | patents or other property right claims or to contest validity of any 229 | such claims; this section has the sole purpose of protecting the 230 | integrity of the free software distribution system, which is 231 | implemented by public license practices. Many people have made 232 | generous contributions to the wide range of software distributed 233 | through that system in reliance on consistent application of that 234 | system; it is up to the author/donor to decide if he or she is willing 235 | to distribute software through any other system and a licensee cannot 236 | impose that choice. 237 | 238 | This section is intended to make thoroughly clear what is believed to 239 | be a consequence of the rest of this License. 240 | 241 | **8.** If the distribution and/or use of the Program is restricted in 242 | certain countries either by patents or by copyrighted interfaces, the 243 | original copyright holder who places the Program under this License 244 | may add an explicit geographical distribution limitation excluding 245 | those countries, so that distribution is permitted only in or among 246 | countries not thus excluded. In such case, this License incorporates 247 | the limitation as if written in the body of this License. 248 | 249 | **9.** The Free Software Foundation may publish revised and/or new 250 | versions of the General Public License from time to time. Such new 251 | versions will be similar in spirit to the present version, but may 252 | differ in detail to address new problems or concerns. 253 | 254 | Each version is given a distinguishing version number. If the Program 255 | specifies a version number of this License which applies to it and 256 | "any later version", you have the option of following the terms and 257 | conditions either of that version or of any later version published by 258 | the Free Software Foundation. If the Program does not specify a 259 | version number of this License, you may choose any version ever 260 | published by the Free Software Foundation. 261 | 262 | **10.** If you wish to incorporate parts of the Program into other 263 | free programs whose distribution conditions are different, write to 264 | the author to ask for permission. For software which is copyrighted by 265 | the Free Software Foundation, write to the Free Software Foundation; 266 | we sometimes make exceptions for this. Our decision will be guided by 267 | the two goals of preserving the free status of all derivatives of our 268 | free software and of promoting the sharing and reuse of software 269 | generally. 270 | 271 | **NO WARRANTY** 272 | 273 | **11.** BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO 274 | WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. 275 | EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR 276 | OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY 277 | KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE 278 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 279 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE 280 | PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME 281 | THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 282 | 283 | **12.** IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN 284 | WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY 285 | AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU 286 | FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR 287 | CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE 288 | PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING 289 | RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A 290 | FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF 291 | SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 292 | DAMAGES. 293 | 294 | ### END OF TERMS AND CONDITIONS 295 | 296 | ### How to Apply These Terms to Your New Programs 297 | 298 | If you develop a new program, and you want it to be of the greatest 299 | possible use to the public, the best way to achieve this is to make it 300 | free software which everyone can redistribute and change under these 301 | terms. 302 | 303 | To do so, attach the following notices to the program. It is safest to 304 | attach them to the start of each source file to most effectively 305 | convey the exclusion of warranty; and each file should have at least 306 | the "copyright" line and a pointer to where the full notice is found. 307 | 308 | one line to give the program's name and an idea of what it does. 309 | Copyright (C) yyyy name of author 310 | 311 | This program is free software; you can redistribute it and/or 312 | modify it under the terms of the GNU General Public License 313 | as published by the Free Software Foundation; either version 2 314 | of the License, or (at your option) any later version. 315 | 316 | This program is distributed in the hope that it will be useful, 317 | but WITHOUT ANY WARRANTY; without even the implied warranty of 318 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 319 | GNU General Public License for more details. 320 | 321 | You should have received a copy of the GNU General Public License 322 | along with this program; if not, write to the Free Software 323 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 324 | 325 | Also add information on how to contact you by electronic and paper 326 | mail. 327 | 328 | If the program is interactive, make it output a short notice like this 329 | when it starts in an interactive mode: 330 | 331 | Gnomovision version 69, Copyright (C) year name of author 332 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details 333 | type `show w'. This is free software, and you are welcome 334 | to redistribute it under certain conditions; type `show c' 335 | for details. 336 | 337 | The hypothetical commands \`show w' and \`show c' should show the 338 | appropriate parts of the General Public License. Of course, the 339 | commands you use may be called something other than \`show w' and 340 | \`show c'; they could even be mouse-clicks or menu items--whatever 341 | suits your program. 342 | 343 | You should also get your employer (if you work as a programmer) or 344 | your school, if any, to sign a "copyright disclaimer" for the program, 345 | if necessary. Here is a sample; alter the names: 346 | 347 | Yoyodyne, Inc., hereby disclaims all copyright 348 | interest in the program `Gnomovision' 349 | (which makes passes at compilers) written 350 | by James Hacker. 351 | 352 | signature of Ty Coon, 1 April 1989 353 | Ty Coon, President of Vice 354 | 355 | This General Public License does not permit incorporating your program 356 | into proprietary programs. If your program is a subroutine library, 357 | you may consider it more useful to permit linking proprietary 358 | applications with the library. If this is what you want to do, use the 359 | [GNU Lesser General Public 360 | License](https://www.gnu.org/licenses/lgpl.html) instead of this 361 | License.### GNU GENERAL PUBLIC LICENSE 362 | 363 | Version 2, June 1991 364 | 365 | Copyright (C) 1989, 1991 Free Software Foundation, Inc. 366 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA 367 | 368 | Everyone is permitted to copy and distribute verbatim copies 369 | of this license document, but changing it is not allowed. 370 | 371 | ### Preamble 372 | 373 | The licenses for most software are designed to take away your freedom 374 | to share and change it. By contrast, the GNU General Public License is 375 | intended to guarantee your freedom to share and change free 376 | software--to make sure the software is free for all its users. This 377 | General Public License applies to most of the Free Software 378 | Foundation's software and to any other program whose authors commit to 379 | using it. (Some other Free Software Foundation software is covered by 380 | the GNU Lesser General Public License instead.) You can apply it to 381 | your programs, too. 382 | 383 | When we speak of free software, we are referring to freedom, not 384 | price. Our General Public Licenses are designed to make sure that you 385 | have the freedom to distribute copies of free software (and charge for 386 | this service if you wish), that you receive source code or can get it 387 | if you want it, that you can change the software or use pieces of it 388 | in new free programs; and that you know you can do these things. 389 | 390 | To protect your rights, we need to make restrictions that forbid 391 | anyone to deny you these rights or to ask you to surrender the rights. 392 | These restrictions translate to certain responsibilities for you if 393 | you distribute copies of the software, or if you modify it. 394 | 395 | For example, if you distribute copies of such a program, whether 396 | gratis or for a fee, you must give the recipients all the rights that 397 | you have. You must make sure that they, too, receive or can get the 398 | source code. And you must show them these terms so they know their 399 | rights. 400 | 401 | We protect your rights with two steps: (1) copyright the software, and 402 | (2) offer you this license which gives you legal permission to copy, 403 | distribute and/or modify the software. 404 | 405 | Also, for each author's protection and ours, we want to make certain 406 | that everyone understands that there is no warranty for this free 407 | software. If the software is modified by someone else and passed on, 408 | we want its recipients to know that what they have is not the 409 | original, so that any problems introduced by others will not reflect 410 | on the original authors' reputations. 411 | 412 | Finally, any free program is threatened constantly by software 413 | patents. We wish to avoid the danger that redistributors of a free 414 | program will individually obtain patent licenses, in effect making the 415 | program proprietary. To prevent this, we have made it clear that any 416 | patent must be licensed for everyone's free use or not licensed at 417 | all. 418 | 419 | The precise terms and conditions for copying, distribution and 420 | modification follow. 421 | 422 | ### TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 423 | 424 | **0.** This License applies to any program or other work which 425 | contains a notice placed by the copyright holder saying it may be 426 | distributed under the terms of this General Public License. The 427 | "Program", below, refers to any such program or work, and a "work 428 | based on the Program" means either the Program or any derivative work 429 | under copyright law: that is to say, a work containing the Program or 430 | a portion of it, either verbatim or with modifications and/or 431 | translated into another language. (Hereinafter, translation is 432 | included without limitation in the term "modification".) Each licensee 433 | is addressed as "you". 434 | 435 | Activities other than copying, distribution and modification are not 436 | covered by this License; they are outside its scope. The act of 437 | running the Program is not restricted, and the output from the Program 438 | is covered only if its contents constitute a work based on the Program 439 | (independent of having been made by running the Program). Whether that 440 | is true depends on what the Program does. 441 | 442 | **1.** You may copy and distribute verbatim copies of the Program's 443 | source code as you receive it, in any medium, provided that you 444 | conspicuously and appropriately publish on each copy an appropriate 445 | copyright notice and disclaimer of warranty; keep intact all the 446 | notices that refer to this License and to the absence of any warranty; 447 | and give any other recipients of the Program a copy of this License 448 | along with the Program. 449 | 450 | You may charge a fee for the physical act of transferring a copy, and 451 | you may at your option offer warranty protection in exchange for a 452 | fee. 453 | 454 | **2.** You may modify your copy or copies of the Program or any 455 | portion of it, thus forming a work based on the Program, and copy and 456 | distribute such modifications or work under the terms of Section 1 457 | above, provided that you also meet all of these conditions: 458 | 459 | 460 | **a)** You must cause the modified files to carry prominent notices 461 | stating that you changed the files and the date of any change. 462 | 463 | 464 | **b)** You must cause any work that you distribute or publish, that in 465 | whole or in part contains or is derived from the Program or any part 466 | thereof, to be licensed as a whole at no charge to all third parties 467 | under the terms of this License. 468 | 469 | 470 | **c)** If the modified program normally reads commands interactively 471 | when run, you must cause it, when started running for such interactive 472 | use in the most ordinary way, to print or display an announcement 473 | including an appropriate copyright notice and a notice that there is 474 | no warranty (or else, saying that you provide a warranty) and that 475 | users may redistribute the program under these conditions, and telling 476 | the user how to view a copy of this License. (Exception: if the 477 | Program itself is interactive but does not normally print such an 478 | announcement, your work based on the Program is not required to print 479 | an announcement.) 480 | 481 | These requirements apply to the modified work as a whole. If 482 | identifiable sections of that work are not derived from the Program, 483 | and can be reasonably considered independent and separate works in 484 | themselves, then this License, and its terms, do not apply to those 485 | sections when you distribute them as separate works. But when you 486 | distribute the same sections as part of a whole which is a work based 487 | on the Program, the distribution of the whole must be on the terms of 488 | this License, whose permissions for other licensees extend to the 489 | entire whole, and thus to each and every part regardless of who wrote 490 | it. 491 | 492 | Thus, it is not the intent of this section to claim rights or contest 493 | your rights to work written entirely by you; rather, the intent is to 494 | exercise the right to control the distribution of derivative or 495 | collective works based on the Program. 496 | 497 | In addition, mere aggregation of another work not based on the Program 498 | with the Program (or with a work based on the Program) on a volume of 499 | a storage or distribution medium does not bring the other work under 500 | the scope of this License. 501 | 502 | **3.** You may copy and distribute the Program (or a work based on it, 503 | under Section 2) in object code or executable form under the terms of 504 | Sections 1 and 2 above provided that you also do one of the following: 505 | 506 | 507 | **a)** Accompany it with the complete corresponding machine-readable 508 | source code, which must be distributed under the terms of Sections 1 509 | and 2 above on a medium customarily used for software interchange; or, 510 | 511 | 512 | **b)** Accompany it with a written offer, valid for at least three 513 | years, to give any third party, for a charge no more than your cost of 514 | physically performing source distribution, a complete machine-readable 515 | copy of the corresponding source code, to be distributed under the 516 | terms of Sections 1 and 2 above on a medium customarily used for 517 | software interchange; or, 518 | 519 | 520 | **c)** Accompany it with the information you received as to the offer 521 | to distribute corresponding source code. (This alternative is allowed 522 | only for noncommercial distribution and only if you received the 523 | program in object code or executable form with such an offer, in 524 | accord with Subsection b above.) 525 | 526 | The source code for a work means the preferred form of the work for 527 | making modifications to it. For an executable work, complete source 528 | code means all the source code for all modules it contains, plus any 529 | associated interface definition files, plus the scripts used to 530 | control compilation and installation of the executable. However, as a 531 | special exception, the source code distributed need not include 532 | anything that is normally distributed (in either source or binary 533 | form) with the major components (compiler, kernel, and so on) of the 534 | operating system on which the executable runs, unless that component 535 | itself accompanies the executable. 536 | 537 | If distribution of executable or object code is made by offering 538 | access to copy from a designated place, then offering equivalent 539 | access to copy the source code from the same place counts as 540 | distribution of the source code, even though third parties are not 541 | compelled to copy the source along with the object code. 542 | 543 | **4.** You may not copy, modify, sublicense, or distribute the Program 544 | except as expressly provided under this License. Any attempt otherwise 545 | to copy, modify, sublicense or distribute the Program is void, and 546 | will automatically terminate your rights under this License. However, 547 | parties who have received copies, or rights, from you under this 548 | License will not have their licenses terminated so long as such 549 | parties remain in full compliance. 550 | 551 | **5.** You are not required to accept this License, since you have not 552 | signed it. However, nothing else grants you permission to modify or 553 | distribute the Program or its derivative works. These actions are 554 | prohibited by law if you do not accept this License. Therefore, by 555 | modifying or distributing the Program (or any work based on the 556 | Program), you indicate your acceptance of this License to do so, and 557 | all its terms and conditions for copying, distributing or modifying 558 | the Program or works based on it. 559 | 560 | **6.** Each time you redistribute the Program (or any work based on 561 | the Program), the recipient automatically receives a license from the 562 | original licensor to copy, distribute or modify the Program subject to 563 | these terms and conditions. You may not impose any further 564 | restrictions on the recipients' exercise of the rights granted herein. 565 | You are not responsible for enforcing compliance by third parties to 566 | this License. 567 | 568 | **7.** If, as a consequence of a court judgment or allegation of 569 | patent infringement or for any other reason (not limited to patent 570 | issues), conditions are imposed on you (whether by court order, 571 | agreement or otherwise) that contradict the conditions of this 572 | License, they do not excuse you from the conditions of this License. 573 | If you cannot distribute so as to satisfy simultaneously your 574 | obligations under this License and any other pertinent obligations, 575 | then as a consequence you may not distribute the Program at all. For 576 | example, if a patent license would not permit royalty-free 577 | redistribution of the Program by all those who receive copies directly 578 | or indirectly through you, then the only way you could satisfy both it 579 | and this License would be to refrain entirely from distribution of the 580 | Program. 581 | 582 | If any portion of this section is held invalid or unenforceable under 583 | any particular circumstance, the balance of the section is intended to 584 | apply and the section as a whole is intended to apply in other 585 | circumstances. 586 | 587 | It is not the purpose of this section to induce you to infringe any 588 | patents or other property right claims or to contest validity of any 589 | such claims; this section has the sole purpose of protecting the 590 | integrity of the free software distribution system, which is 591 | implemented by public license practices. Many people have made 592 | generous contributions to the wide range of software distributed 593 | through that system in reliance on consistent application of that 594 | system; it is up to the author/donor to decide if he or she is willing 595 | to distribute software through any other system and a licensee cannot 596 | impose that choice. 597 | 598 | This section is intended to make thoroughly clear what is believed to 599 | be a consequence of the rest of this License. 600 | 601 | **8.** If the distribution and/or use of the Program is restricted in 602 | certain countries either by patents or by copyrighted interfaces, the 603 | original copyright holder who places the Program under this License 604 | may add an explicit geographical distribution limitation excluding 605 | those countries, so that distribution is permitted only in or among 606 | countries not thus excluded. In such case, this License incorporates 607 | the limitation as if written in the body of this License. 608 | 609 | **9.** The Free Software Foundation may publish revised and/or new 610 | versions of the General Public License from time to time. Such new 611 | versions will be similar in spirit to the present version, but may 612 | differ in detail to address new problems or concerns. 613 | 614 | Each version is given a distinguishing version number. If the Program 615 | specifies a version number of this License which applies to it and 616 | "any later version", you have the option of following the terms and 617 | conditions either of that version or of any later version published by 618 | the Free Software Foundation. If the Program does not specify a 619 | version number of this License, you may choose any version ever 620 | published by the Free Software Foundation. 621 | 622 | **10.** If you wish to incorporate parts of the Program into other 623 | free programs whose distribution conditions are different, write to 624 | the author to ask for permission. For software which is copyrighted by 625 | the Free Software Foundation, write to the Free Software Foundation; 626 | we sometimes make exceptions for this. Our decision will be guided by 627 | the two goals of preserving the free status of all derivatives of our 628 | free software and of promoting the sharing and reuse of software 629 | generally. 630 | 631 | **NO WARRANTY** 632 | 633 | **11.** BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO 634 | WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. 635 | EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR 636 | OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY 637 | KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE 638 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 639 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE 640 | PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME 641 | THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 642 | 643 | **12.** IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN 644 | WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY 645 | AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU 646 | FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR 647 | CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE 648 | PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING 649 | RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A 650 | FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF 651 | SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 652 | DAMAGES. 653 | 654 | ### END OF TERMS AND CONDITIONS 655 | 656 | ### How to Apply These Terms to Your New Programs 657 | 658 | If you develop a new program, and you want it to be of the greatest 659 | possible use to the public, the best way to achieve this is to make it 660 | free software which everyone can redistribute and change under these 661 | terms. 662 | 663 | To do so, attach the following notices to the program. It is safest to 664 | attach them to the start of each source file to most effectively 665 | convey the exclusion of warranty; and each file should have at least 666 | the "copyright" line and a pointer to where the full notice is found. 667 | 668 | one line to give the program's name and an idea of what it does. 669 | Copyright (C) yyyy name of author 670 | 671 | This program is free software; you can redistribute it and/or 672 | modify it under the terms of the GNU General Public License 673 | as published by the Free Software Foundation; either version 2 674 | of the License, or (at your option) any later version. 675 | 676 | This program is distributed in the hope that it will be useful, 677 | but WITHOUT ANY WARRANTY; without even the implied warranty of 678 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 679 | GNU General Public License for more details. 680 | 681 | You should have received a copy of the GNU General Public License 682 | along with this program; if not, write to the Free Software 683 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 684 | 685 | Also add information on how to contact you by electronic and paper 686 | mail. 687 | 688 | If the program is interactive, make it output a short notice like this 689 | when it starts in an interactive mode: 690 | 691 | Gnomovision version 69, Copyright (C) year name of author 692 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details 693 | type `show w'. This is free software, and you are welcome 694 | to redistribute it under certain conditions; type `show c' 695 | for details. 696 | 697 | The hypothetical commands \`show w' and \`show c' should show the 698 | appropriate parts of the General Public License. Of course, the 699 | commands you use may be called something other than \`show w' and 700 | \`show c'; they could even be mouse-clicks or menu items--whatever 701 | suits your program. 702 | 703 | You should also get your employer (if you work as a programmer) or 704 | your school, if any, to sign a "copyright disclaimer" for the program, 705 | if necessary. Here is a sample; alter the names: 706 | 707 | Yoyodyne, Inc., hereby disclaims all copyright 708 | interest in the program `Gnomovision' 709 | (which makes passes at compilers) written 710 | by James Hacker. 711 | 712 | signature of Ty Coon, 1 April 1989 713 | Ty Coon, President of Vice 714 | 715 | This General Public License does not permit incorporating your program 716 | into proprietary programs. If your program is a subroutine library, 717 | you may consider it more useful to permit linking proprietary 718 | applications with the library. If this is what you want to do, use the 719 | [GNU Lesser General Public 720 | License](https://www.gnu.org/licenses/lgpl.html) instead of this 721 | License. 722 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WP Tests Starter 2 | Note: You're reading the documentation for the development branch towards version 2.0.0. You'll find the documentation 3 | for version 1.0 at the branch [version-1](https://github.com/inpsyde/WP-Tests-Starter/tree/version-1) 4 | 5 | Wp Tests starter is a library that assist you in setting up _integration tests_ for your plugin or library with WordPress 6 | core using the official [wordpress-develop repository](https://github.com/inpsyde/wordpress-dev). The main difference to 7 | unit tests is that you don't need (and typically don't want) to mock any WordPress function. Instead, you have a fully 8 | booted WordPress core in place with an actual connection to a database server. 9 | 10 | So if you're mapping your objects to WordPress posts in a ORM-style way your integration test would look like this: 11 | 12 | ```php 13 | public function testPersistBook(): void { 14 | 15 | $book = new Book('The Da Vinci Code', 'Dan Brown', '2003'); 16 | $testee = new BookRepository($GLOBALS['wpdb']); 17 | $testee->persist($book); // maps book to WP_Post object and post meta 18 | 19 | self::assertGreaterThan(0, $book->id()); 20 | 21 | $wpPost = get_post($book->id()); 22 | 23 | self::assertSame('The Da Vinci Code', $wpPost->post_title); 24 | self::assertSame('2003', get_post_meta($book->id(), '_publishing_year', true)); 25 | self::assertSame('Dan Brown', get_post_meta($book->id(), '_author', true)); 26 | } 27 | ``` 28 | 29 | No mocks required. Just WordPress working inside a PHPUnit test case. 30 | 31 | ## Installation 32 | 33 | In order to use Wp Tests Starter you need: a PHP environment with Composer and a MySQL server with a _dedicated test database_. This database should be completely ephemeral, so do not use any database that contains important data. You'll also need the following four Composer packages installed as dev dependencies: 34 | 35 | * `inpsyde/wp-tests-starter` 36 | * `yoast/phpunit-polyfills` 37 | * `wordpress/wordpress` taken from [wordpress-develop repository](https://github.com/inpsyde/wordpress-dev) 38 | * `phpunit/phpunit` 39 | 40 | As the last one is not available on packagist.org, you'll have to add the repo manually to your composer.json file by adding: 41 | 42 | ```json 43 | "repositories": [ 44 | { 45 | "type": "vcs", 46 | "url": "https://github.com/WordPress/wordpress-develop" 47 | } 48 | ] 49 | ``` 50 | 51 | Now you can run 52 | 53 | composer require --dev inpsyde/wp-tests-starter yoast/phpunit-polyfills wordpress/wordpress phpunit/phpunit 54 | 55 | Note that this will take a while as Composer will analyze the entire WordPress repository on GitHub. (Once a composer.lock is in place it will go faster on the next install run) 56 | 57 | ## Setup your tests 58 | 59 | To set up your PHPUnit tests you need two files in place: `phpunit.xml.dist` and a `boostrap.php` which gets loaded by 60 | PHPUnit before your actual tests are executed. The shown examples of these two files assume a directory structure of your 61 | library like this: 62 | 63 | ├ src/ 64 | | └ MyModule.php 65 | ├ tests/ 66 | | ├integration/ 67 | | | └ MyModuleTest.php 68 | | └boostrap.php 69 | ├ vendor/ 70 | ├ composer.json 71 | └ phpunit.xml.dist 72 | 73 | The following example of the phpunit.xml.dist file tells PHPUnit where the test files resides and contains the database 74 | credentials as an environment variable: 75 | 76 | ```xml 77 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | ./tests/integration 87 | 88 | 89 | 90 | ``` 91 | 92 | The `tests/boostrap.php` finally loads Wp Tests Starter and WordPress: 93 | 94 | ```php 95 | testAsMultisite() 116 | // boostrap your plugin or module code 117 | ->addActivePlugin(static function() { 118 | (new MyModule())->init(); 119 | }) 120 | // add filters early 121 | ->addFilter('my_app.modules', static function(array $modules): array { 122 | // whatever, it's just an example 123 | return $modules; 124 | }) 125 | //finally load WordPress 126 | ->bootstrap(); 127 | ``` 128 | 129 | These files just show a short way to run integration tests with WP Tests Starter. You have several other configuration 130 | options available through the methods of the `WpTestsStarter` object. 131 | 132 | ## Run PHPUnit 133 | 134 | With this configuration in place you can run PHPUnit to execute all test classes in `tests/integration` with 135 | 136 | vendor/bin/phpunit 137 | 138 | On every run, WP Starter will write the configuration to `vendor/wordpress/wordpress/wp-config.php` and load the WordPress 139 | internal bootstrap script which ensures installation of the database tables for example. 140 | 141 | ## Configuration 142 | 143 | ### DB Url 144 | 145 | In order to not have to maintain several environment variables you can pass all databse credentials and options via a 146 | single parameter like this: 147 | 148 | mysql://user:password@localhost:3306/test_db?table_prefix=wp_tests_&charset=utf8mb4&collation=utf8_general_ci 149 | 150 | This URL can be passed to `WpTestsStarter` either by constructor parameter or by `useDbUrl()` method: 151 | 152 | ```php 153 | useDbUrl($dbUrl); 158 | ``` 159 | 160 | The URL example above would turn into the following WordPress constants and globals: 161 | 162 | ```php 163 | useDbHost('localhost') 179 | ->useDbUser('user'); 180 | // and so on 181 | ``` 182 | 183 | ## Copyright and License 184 | 185 | This package is [free software](https://www.gnu.org/philosophy/free-sw.en.html) distributed under the terms of the GNU General Public License version 2 or (at your option) any later version. For the full license, see [LICENSE](./LICENSE). 186 | 187 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "inpsyde/wp-tests-starter", 3 | "description": "A package that helps you setting up WordPress integration test environments quickly.", 4 | "keywords": [ 5 | "WordPress", 6 | "Tests", 7 | "Integration tests" 8 | ], 9 | "license": "GPL-2.0-or-later", 10 | "authors": [ 11 | { 12 | "name": "Syde GmbH", 13 | "homepage": "https://syde.com/", 14 | "email": "hello@syde.com", 15 | "role": "Company" 16 | }, 17 | { 18 | "name": "David Naber", 19 | "email": "d.naber@syde.com", 20 | "role": "Developer" 21 | } 22 | ], 23 | "require": { 24 | "php": ">=7.4" 25 | }, 26 | "require-dev": { 27 | "inpsyde/php-coding-standards": "^1.0", 28 | "phpunit/phpunit": "^9.5", 29 | "wordpress/wordpress": "^6.0", 30 | "yoast/phpunit-polyfills": "^1.0" 31 | }, 32 | "autoload": { 33 | "psr-4": { 34 | "Inpsyde\\WpTestsStarter\\": "src/" 35 | } 36 | }, 37 | "config": { 38 | "allow-plugins": { 39 | "dealerdirect/phpcodesniffer-composer-installer": true 40 | } 41 | }, 42 | "repositories": [ 43 | { 44 | "type": "vcs", 45 | "url": "https://github.com/WordPress/wordpress-develop" 46 | } 47 | ] 48 | } 49 | -------------------------------------------------------------------------------- /phpcs.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | ./src/ 4 | ./tests/ 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 21 | 22 | 23 | 24 | vendor/* 25 | 26 | -------------------------------------------------------------------------------- /phpunit-integration.xml.dist: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | 12 | 13 | 14 | ./tests/WpIntegration 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | ./tests/Unit 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/Exception/RuntimeException.php: -------------------------------------------------------------------------------- 1 | null, 30 | 'user' => null, 31 | 'password' => null, 32 | 'db' => null, 33 | 'table_prefix' => null, 34 | 'charset' => null, 35 | 'collation' => null, 36 | ]; 37 | 38 | $parts = parse_url($dbUrl); 39 | if (! is_array($parts)) { 40 | throw new RuntimeException("Please provide a valid dbUrl"); 41 | } 42 | 43 | if (array_key_exists('scheme', $parts) && $parts['scheme'] !== 'mysql') { 44 | throw new RuntimeException("Currently only 'mysql' databases are supported"); 45 | } 46 | 47 | if (array_key_exists('host', $parts)) { 48 | $credentials['host'] = $parts['host']; 49 | if (array_key_exists('port', $parts)) { 50 | $credentials['host'] .= ':' . $parts['port']; 51 | } 52 | } 53 | 54 | array_key_exists('user', $parts) and $credentials['user'] = $parts['user']; 55 | array_key_exists('pass', $parts) and $credentials['password'] = $parts['pass']; 56 | array_key_exists('path', $parts) and $credentials['db'] = trim($parts['path'], '/'); 57 | 58 | if (! array_key_exists('query', $parts) || empty($parts['query'])) { 59 | return $credentials; 60 | } 61 | 62 | $query = []; 63 | parse_str($parts['query'], $query); 64 | 65 | array_key_exists('table_prefix', $query) and $credentials['table_prefix'] = $query['table_prefix']; 66 | array_key_exists('charset', $query) and $credentials['charset'] = $query['charset']; 67 | array_key_exists('collation', $query) and $credentials['collation'] = $query['collation']; 68 | 69 | return $credentials; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/Helper/SaltGenerator.php: -------------------------------------------------------------------------------- 1 | baseDir = rtrim($baseDir, '\\/'); 40 | $this->saltGenerator = $saltGenerator ?? new SaltGenerator(); 41 | $this->dbUrlParser = $dbUrlParser ?? new DbUrlParser(); 42 | 43 | // set some common defaults 44 | $this->useDbHost('localhost') 45 | ->useSiteDomain('example.tld') 46 | ->useEmail('admin@example.tld') 47 | ->useSiteTitle('Wp Tests Starter') 48 | ->usePhpBinary('/usr/bin/php') 49 | ->useTablePrefix('wp_tests_') 50 | ->useAbsPath($this->baseDir . '/src/') 51 | ->generateSalts(); 52 | 53 | $dbUrl and $this->useDbUrl($dbUrl); 54 | } 55 | 56 | /** 57 | * Loading the WordPress testing bootstrap 58 | */ 59 | public function bootstrap() 60 | { 61 | $this->defineConstants(); 62 | $this->declareGlobals(); 63 | $this->writeConfigFile(); 64 | $this->installLivePlugin(); 65 | 66 | $wpBoostrapFile = $this->baseDir . '/tests/phpunit/includes/bootstrap.php'; 67 | require_once $wpBoostrapFile; 68 | } 69 | 70 | public function useConst(string $const, $value): self 71 | { 72 | $this->constants[$const] = $value; 73 | 74 | return $this; 75 | } 76 | 77 | public function useGlobalVar(string $var, $value): self 78 | { 79 | $this->globalsFactories[] = static function () use ($var, $value): void { 80 | $GLOBALS[$var] = $value; 81 | }; 82 | 83 | return $this; 84 | } 85 | 86 | public function useDbUrl(string $dbUrl): self 87 | { 88 | $credentials = $this->dbUrlParser->parse($dbUrl); 89 | 90 | $credentials['host'] and $this->useDbHost($credentials['host']); 91 | $credentials['user'] and $this->useDbUser($credentials['user']); 92 | $credentials['password'] and $this->useDbPassword($credentials['password']); 93 | $credentials['db'] and $this->useDbName($credentials['db']); 94 | $credentials['table_prefix'] and $this->useTablePrefix($credentials['table_prefix']); 95 | $credentials['charset'] and $this->useDbCharset($credentials['charset']); 96 | $credentials['collation'] and $this->useDbCollation($credentials['collation']); 97 | 98 | return $this; 99 | } 100 | 101 | public function useAbsPath(?string $abspath = null): self 102 | { 103 | return $this->useConst('ABSPATH', $abspath); 104 | } 105 | 106 | public function useDbName(string $dbName): self 107 | { 108 | return $this->useConst('DB_NAME', $dbName); 109 | } 110 | 111 | public function useDbHost(string $dbHost): self 112 | { 113 | return $this->useConst('DB_HOST', $dbHost); 114 | } 115 | 116 | public function useDbUser(string $dbUser): self 117 | { 118 | return $this->useConst('DB_USER', $dbUser); 119 | } 120 | 121 | public function useDbPassword(string $dbPassword): self 122 | { 123 | return $this->useConst('DB_PASSWORD', $dbPassword); 124 | } 125 | 126 | public function useDbCharset(string $dbCharset): self 127 | { 128 | return $this->useConst('DB_CHARSET', $dbCharset); 129 | } 130 | 131 | public function useDbCollation(string $dbCollation): self 132 | { 133 | return $this->useConst('DB_COLLATE', $dbCollation); 134 | } 135 | 136 | public function useDebugMode(bool $wpDebug): self 137 | { 138 | return $this->useConst('WP_DEBUG', $wpDebug); 139 | } 140 | 141 | public function useSiteDomain(string $domain): self 142 | { 143 | return $this->useConst('WP_TESTS_DOMAIN', $domain); 144 | } 145 | 146 | public function useEmail(string $email): self 147 | { 148 | return $this->useConst('WP_TESTS_EMAIL', $email); 149 | } 150 | 151 | public function useSiteTitle(string $title): self 152 | { 153 | return $this->useConst('WP_TESTS_TITLE', $title); 154 | } 155 | 156 | public function usePhpBinary(string $binary): self 157 | { 158 | return $this->useConst('WP_PHP_BINARY', $binary); 159 | } 160 | 161 | public function testAsMultisite(bool $isMultisite = true): self 162 | { 163 | return $this->useConst('WP_TESTS_MULTISITE', $isMultisite); 164 | } 165 | 166 | public function useWpPluginDir(string $dir): self 167 | { 168 | $dir = rtrim($dir, '\\/'); 169 | 170 | return $this->useConst('WP_PLUGIN_DIR', $dir); 171 | } 172 | 173 | /** 174 | * add action before WP core is loaded 175 | */ 176 | public function addAction(string $action, callable $listener, int $priority): self 177 | { 178 | return $this->addFilter($action, $listener, $priority); 179 | } 180 | 181 | /** 182 | * add filter before WP core is loaded 183 | */ 184 | public function addFilter(string $filter, callable $listener, int $priority = 10): self 185 | { 186 | isset($GLOBALS['wp_filter']) or $GLOBALS['wp_filter'] = []; 187 | isset($GLOBALS['wp_filter'][$filter]) or $GLOBALS['wp_filter'][$filter] = []; 188 | isset($GLOBALS['wp_filter'][$filter][$priority]) or $GLOBALS['wp_filter'][$filter][$priority] = []; 189 | 190 | $GLOBALS['wp_filter'][$filter][$priority][] = [ 191 | 'accepted_args' => PHP_INT_MAX, 192 | 'function' => $listener 193 | ]; 194 | 195 | return $this; 196 | } 197 | 198 | /** 199 | * @param string $plugin a plugin file relative to WP's plugin directory like 'directory/plugin-file.php' 200 | */ 201 | public function addActivePlugin(string $plugin): self 202 | { 203 | $this->globalsFactories[] = static function () use ($plugin): void { 204 | if (! isset($GLOBALS['wp_tests_options'])) { 205 | $GLOBALS['wp_tests_options'] = []; 206 | } 207 | 208 | if (! isset($GLOBALS['wp_tests_options']['active_plugins'])) { 209 | $GLOBALS['wp_tests_options']['active_plugins'] = []; 210 | } 211 | 212 | if (in_array($plugin, $GLOBALS['wp_tests_options']['active_plugins'])) { 213 | return; 214 | } 215 | 216 | $GLOBALS['wp_tests_options']['active_plugins'][] = $plugin; 217 | }; 218 | 219 | return $this; 220 | } 221 | 222 | public function addLivePlugin(callable $plugin): self 223 | { 224 | self::$livePlugins[] = $plugin; 225 | 226 | return $this; 227 | } 228 | 229 | public function useTablePrefix(string $prefix): self 230 | { 231 | return $this->useGlobalVar('table_prefix', $prefix); 232 | } 233 | 234 | public static function runLivePlugins(): void 235 | { 236 | foreach(self::$livePlugins as $livePlugin) { 237 | $livePlugin(); 238 | } 239 | } 240 | 241 | private function installLivePlugin(): void 242 | { 243 | if(!self::$livePlugins) { 244 | return; 245 | } 246 | 247 | $pluginCode = <<<'PHP' 248 | muPluginDir(); 268 | if(!is_dir($muPluginDir)) { 269 | mkdir($muPluginDir, 0755, true); 270 | } 271 | 272 | $pluginFile = $muPluginDir . '/wp-tests-starter-live-plugin.php'; 273 | file_put_contents( 274 | $pluginFile, 275 | $pluginCode 276 | ); 277 | } 278 | 279 | private function defineConstants(): void 280 | { 281 | foreach ($this->constants as $constant => $value) { 282 | if (defined($constant)) { 283 | continue; 284 | } 285 | 286 | define($constant, $value); 287 | } 288 | } 289 | 290 | private function declareGlobals(): void 291 | { 292 | foreach ($this->globalsFactories as $factory) { 293 | $factory(); 294 | } 295 | } 296 | 297 | private function generateSalts() 298 | { 299 | $saltConstants = [ 300 | 'AUTH_KEY', 301 | 'SECURE_AUTH_KEY', 302 | 'LOGGED_IN_KEY', 303 | 'NONCE_KEY', 304 | 'SECURE_AUTH_SALT', 305 | 'LOGGED_IN_SALT', 306 | 'NONCE_SALT', 307 | 'AUTH_KEY', 308 | ]; 309 | 310 | foreach ($saltConstants as $constant) { 311 | $this->useConst( 312 | $constant, 313 | $this->saltGenerator->generateSalt() 314 | ); 315 | } 316 | } 317 | 318 | private function muPluginDir(): ?string 319 | { 320 | if(defined('WPMU_PLUGIN_DIR')) { 321 | return (string) WPMU_PLUGIN_DIR; 322 | } 323 | 324 | if(array_key_exists('WPMU_PLUGIN_DIR', $this->constants)) { 325 | return (string) $this->constants['WPMU_PLUGIN_DIR']; 326 | } 327 | 328 | if(defined('WP_CONTENT_DIR')) { 329 | return WP_CONTENT_DIR . '/mu-plugins'; 330 | } 331 | 332 | if(array_key_exists('WP_CONTENT_DIR', $this->constants)) { 333 | return $this->constants['WP_CONTENT_DIR'] . '/mu-plugins'; 334 | } 335 | 336 | return $this->baseDir . '/src/wp-content/mu-plugins'; 337 | } 338 | 339 | private function writeConfigFile() 340 | { 341 | $configFile = $this->getConfigFile(); 342 | if (! file_exists($configFile)) { 343 | touch($configFile); 344 | } 345 | 346 | /** 347 | * We have to persist all dynamic configuration (constants and globals) in a wp-config.php 348 | * as the WordPress internal boostrap process runs a sub process for the installation (setup DB tables) 349 | */ 350 | $constantsDefinition = $this->getDefinedConstantsCode(); 351 | $content = <<baseDir . '/wp-tests-config.php'; 363 | } 364 | 365 | private function getDefinedConstantsCode(): string 366 | { 367 | $code = ''; 368 | foreach ($this->constants as $constant => $value) { 369 | $constant = $this->escapePhpString($constant); 370 | $value = $this->escapePhpString($value); 371 | $code .= "if ( ! defined( '{$constant}' ) )\n"; 372 | $code .= "\tdefine( '{$constant}', '{$value}' );\n"; 373 | } 374 | 375 | return $code; 376 | } 377 | 378 | private function escapePhpString($value): string 379 | { 380 | $value = str_replace( 381 | [''], 382 | '', 383 | (string) $value 384 | ); 385 | $value = addcslashes($value, "'\\"); 386 | 387 | return $value; 388 | } 389 | } 390 | -------------------------------------------------------------------------------- /tests/Unit/Helper/DbUrlParserTest.php: -------------------------------------------------------------------------------- 1 | parse($dbUrl) 23 | ); 24 | } 25 | 26 | /** 27 | * @see testParse 28 | */ 29 | public function parseData(): iterable 30 | { 31 | 32 | yield 'Test entire parameter set' => [ 33 | 'dbUrl' => 'mysql://john:T0p3ecr3t@localhost/wordpress?table_prefix=wp_tests_&charset=iso88591&collation=latin1_swedish_ci', 34 | 'expected' => [ 35 | 'user' => 'john', 36 | 'host' => 'localhost', 37 | 'password' => 'T0p3ecr3t', 38 | 'db' => 'wordpress', 39 | 'table_prefix' => 'wp_tests_', 40 | 'charset' => 'iso88591', 41 | 'collation' => 'latin1_swedish_ci' 42 | ] 43 | ]; 44 | 45 | yield 'Test entire parameter set with port number' => [ 46 | 'dbUrl' => 'mysql://john:T0p3ecr3t@localhost:3306/wordpress?table_prefix=wp_tests_&charset=iso88591&collation=latin1_swedish_ci', 47 | 'expected' => [ 48 | 'user' => 'john', 49 | 'host' => 'localhost:3306', 50 | 'password' => 'T0p3ecr3t', 51 | 'db' => 'wordpress', 52 | 'table_prefix' => 'wp_tests_', 53 | 'charset' => 'iso88591', 54 | 'collation' => 'latin1_swedish_ci' 55 | ] 56 | ]; 57 | 58 | yield 'Only user and host' => [ 59 | 'dbUrl' => 'mysql://john@localhost', 60 | 'expected' => [ 61 | 'user' => 'john', 62 | 'host' => 'localhost', 63 | 'password' => null, 64 | 'db' => null, 65 | 'table_prefix' => null, 66 | 'charset' => null, 67 | 'collation' => null, 68 | ] 69 | ]; 70 | 71 | yield 'Nothing at all' => [ 72 | 'dbUrl' => 'mysql:', 73 | 'expected' => [ 74 | 'user' => null, 75 | 'host' => null, 76 | 'password' => null, 77 | 'db' => null, 78 | 'table_prefix' => null, 79 | 'charset' => null, 80 | 'collation' => null, 81 | ] 82 | ]; 83 | } 84 | 85 | /** 86 | * @dataProvider parseThrowsExceptionData 87 | */ 88 | public function testParseThrowsException(string $url, string $expectedMessage): void 89 | { 90 | $testee = new DbUrlParser(); 91 | 92 | self::expectException(RuntimeException::class); 93 | self::expectExceptionMessage($expectedMessage); 94 | 95 | $testee->parse($url); 96 | } 97 | 98 | /** 99 | * @see testParseThrowsException 100 | */ 101 | public function parseThrowsExceptionData(): iterable 102 | { 103 | 104 | yield 'Test invalid URL' => [ 105 | 'mysql://', 106 | "Please provide a valid dbUrl", 107 | ]; 108 | 109 | yield 'Test unsupported scheme' => [ 110 | 'mssql:', 111 | "Currently only 'mysql' databases are supported", 112 | ]; 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /tests/Unit/Helper/SaltGeneratorTest.php: -------------------------------------------------------------------------------- 1 | generateSalt($length); 20 | 21 | self::assertMatchesRegularExpression( 22 | '~^[\x20-\x7E]{' . preg_quote((string)$length) . '}$~', 23 | $salt 24 | ); 25 | } 26 | 27 | public function generateSaltTestProvider(): array 28 | { 29 | return [ 30 | ['length' => 1], 31 | ['length' => 10], 32 | ['length' => 42], 33 | ]; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /tests/Unit/WpTestsStarterTest.php: -------------------------------------------------------------------------------- 1 | bootstrap(); 25 | 26 | self::assertFileExists( 27 | $wpTestConfig 28 | ); 29 | $config_data = file_get_contents($wpTestConfig); 30 | 31 | self::assertMatchesRegularExpression( 32 | '~define\(\s\'ABSPATH\',\s\'[^\']+\'~', 33 | $config_data 34 | ); 35 | self::assertInstanceOf( 36 | \wpdb::class, 37 | $GLOBALS['wpdb'] 38 | ); 39 | self::assertTrue( 40 | $GLOBALS['wpdb']->check_connection() 41 | ); 42 | } 43 | 44 | /** 45 | * @runInSeparateProcess 46 | */ 47 | public function testBoostrapWithSetters(): void 48 | { 49 | $baseDir = dirname(dirname(__DIR__)) . '/vendor/wordpress/wordpress'; 50 | $wpTestConfig = $baseDir . '/wp-tests-config.php'; 51 | 52 | $credentials = (new DbUrlParser()) 53 | ->parse(getenv('WPTS_DB_URL')); 54 | 55 | $testee = new WpTestsStarter($baseDir); 56 | 57 | $credentials['host'] and $testee->useDbHost($credentials['host']); 58 | $credentials['user'] and $testee->useDbUser($credentials['user']); 59 | $credentials['password'] and $testee->useDbPassword($credentials['password']); 60 | $credentials['db'] and $testee->useDbName($credentials['db']); 61 | $credentials['table_prefix'] and $testee->useTablePrefix($credentials['table_prefix']); 62 | $credentials['charset'] and $testee->useDbCharset($credentials['charset']); 63 | $credentials['collation'] and $testee->useDbCollation($credentials['collation']); 64 | $testee->bootstrap(); 65 | 66 | self::assertFileExists( 67 | $wpTestConfig 68 | ); 69 | $config_data = file_get_contents($wpTestConfig); 70 | 71 | self::assertMatchesRegularExpression( 72 | '~define\(\s\'ABSPATH\',\s\'[^\']+\'~', 73 | $config_data 74 | ); 75 | self::assertInstanceOf( 76 | \wpdb::class, 77 | $GLOBALS['wpdb'] 78 | ); 79 | self::assertTrue( 80 | $GLOBALS['wpdb']->check_connection() 81 | ); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /tests/WpIntegration/WpTestsStarterTest.php: -------------------------------------------------------------------------------- 1 | $arg; 33 | $testee->useWpPluginDir(self::$pluginDir) 34 | ->addActivePlugin(self::$testPlugin) 35 | ->addLivePlugin(static function(): void { 36 | defined('WPTS_LIVE_PLUGIN_RUN') or define('WPTS_LIVE_PLUGIN_RUN', true); 37 | }) 38 | ->addFilter('post_link', $dynamicListener) 39 | ->addAction('template_redirect', $dynamicListener, 30) 40 | ->bootstrap(); 41 | 42 | // test if the environment is available 43 | self::assertTrue( 44 | class_exists(\WP_UnitTestCase::class), 45 | 'Class \WP_UnitTestCase does not exist.' 46 | ); 47 | 48 | $this->wpDbAssertions(); 49 | $this->installedAssertions(); 50 | $this->pluginAssertions(); 51 | $this->livePluginAssertions(); 52 | $this->listenerAssertion($dynamicListener); 53 | } 54 | 55 | private function wpDbAssertions(): void 56 | { 57 | self::assertInstanceOf( 58 | \wpdb::class, 59 | $GLOBALS['wpdb'] 60 | ); 61 | self::assertTrue( 62 | $GLOBALS['wpdb']->check_connection() 63 | ); 64 | } 65 | 66 | private function installedAssertions(): void 67 | { 68 | $dbTables = $GLOBALS['wpdb']->get_results( 69 | 'SHOW TABLES', 70 | \ARRAY_N 71 | ); 72 | $tablesFlat = []; 73 | foreach ($dbTables as $row) { 74 | $tablesFlat[] = $row[0]; 75 | } 76 | 77 | $optionTable = $GLOBALS['table_prefix'] . 'options'; 78 | self::assertTrue( 79 | in_array($optionTable, $tablesFlat), 80 | "Table {$optionTable} does not exist!" 81 | ); 82 | } 83 | 84 | private function pluginAssertions(): void 85 | { 86 | self::assertArrayHasKey( 87 | 'wp_tests_options', 88 | $GLOBALS 89 | ); 90 | self::assertArrayHasKey( 91 | 'active_plugins', 92 | $GLOBALS['wp_tests_options'] 93 | ); 94 | self::assertContains( 95 | self::$testPlugin, 96 | $GLOBALS['wp_tests_options']['active_plugins'] 97 | ); 98 | self::assertTrue( 99 | defined('WPTS_TEST_PLUGIN_LOADED') 100 | ); 101 | } 102 | 103 | private function livePluginAssertions(): void 104 | { 105 | self::assertFileExists( 106 | WPMU_PLUGIN_DIR . '/wp-tests-starter-live-plugin.php' 107 | ); 108 | self::assertTrue( 109 | defined('WPTS_LIVE_PLUGIN_RUN') 110 | ); 111 | } 112 | 113 | private function listenerAssertion(callable $listener): void 114 | { 115 | self::assertSame( 116 | 10, 117 | has_filter('post_link', $listener) 118 | ); 119 | self::assertSame( 120 | 30, 121 | has_action('template_redirect', $listener) 122 | ); 123 | // test that we don't mess up the structure of $GLOBALS['wp_filter'] 124 | self::assertSame( 125 | 9, 126 | has_filter('the_content', 'do_blocks') 127 | ); 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /tests/bootstrap.dist.php: -------------------------------------------------------------------------------- 1 |