├── .env.example ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── build.dist.xml ├── build.properties.dist ├── build.xml ├── composer.json ├── drush ├── Commands │ └── PolicyCommands.php ├── README.md ├── drush.yml.dist └── sites │ └── self.site.yml ├── load.environment.php ├── phpcs-ruleset.xml.dist ├── phpunit.xml.dist ├── scripts ├── composer │ └── ScriptHandler.php └── travis-ci │ └── run-test.sh ├── src └── Phing │ ├── PhpCodeSnifferConfigurationTask.php │ └── PhpUnitConfigurationTask.php └── tests ├── behat ├── behat.yml.dist └── features ├── authentication.feature └── bootstrap └── FeatureContext.php /.env.example: -------------------------------------------------------------------------------- 1 | # 2 | # Copy and rename this file to .env at root of this project. 3 | # 4 | 5 | # A common use case is to supply database creds via the environment. Edit settings.php 6 | # like so: 7 | # 8 | # $databases['default']['default'] = [ 9 | # 'database' => getenv('MYSQL_DATABASE'), 10 | # 'driver' => 'mysql', 11 | # 'host' => getenv('MYSQL_HOSTNAME'), 12 | # 'namespace' => 'Drupal\\Core\\Database\\Driver\\mysql', 13 | # 'password' => getenv('MYSQL_PASSWORD'), 14 | # 'port' => getenv('MYSQL_PORT'), 15 | # 'prefix' => '', 16 | # 'username' => getenv('MYSQL_USER'), 17 | # ]; 18 | # 19 | # Uncomment and populate as needed. 20 | # MYSQL_DATABASE= 21 | # MYSQL_HOSTNAME= 22 | # MYSQL_PASSWORD= 23 | # MYSQL_PORT= 24 | # MYSQL_USER= 25 | 26 | # Another common use case is to set Drush's --uri via environment. 27 | # DRUSH_OPTIONS_URI=http://example.com 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore directories generated by Composer 2 | /bin/ 3 | /drush/contrib/ 4 | /vendor/ 5 | /web/core/ 6 | /web/libraries/ 7 | /web/modules/contrib/ 8 | /web/profiles/contrib/ 9 | /web/themes/contrib/ 10 | 11 | # Ignore sensitive information 12 | /web/sites/*/settings.php 13 | /web/sites/*/settings.local.php 14 | 15 | # Ignore Drupal's file directory 16 | /web/sites/*/files/ 17 | 18 | # Ignore SimpleTest multi-site environment. 19 | /web/sites/simpletest/ 20 | 21 | # Ignore environment specific settings 22 | /build.local.xml 23 | /build.properties.local 24 | /drush/drush.yml 25 | /phpcs.xml 26 | /tests/behat.yml 27 | /web/phpunit.xml 28 | /web/sites/default/services.yml 29 | 30 | # Ignore files generated by PhpStorm 31 | /.idea/ 32 | 33 | # Ignore .env files as they are personal 34 | /.env 35 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | php: 4 | - 7.0 5 | - 7.1 6 | - 7.2 7 | - 7.3 8 | 9 | sudo: false 10 | 11 | mysql: 12 | database: myproject 13 | username: root 14 | encoding: utf8 15 | 16 | env: 17 | - TEST=PHP_CodeSniffer 18 | - TEST=PHPUnit 19 | - TEST=Behat 20 | 21 | matrix: 22 | exclude: 23 | - php: 7.0 24 | env: TEST=PHP_CodeSniffer 25 | - php: 7.1 26 | env: TEST=PHP_CodeSniffer 27 | - php: 7.3 28 | env: TEST=PHP_CodeSniffer 29 | 30 | matrix: 31 | allow_failures: 32 | - php: 7.3 33 | 34 | before_install: 35 | # Remove Xdebug as we don't need it and it causes "PHP Fatal error: Maximum 36 | # function nesting level of '256' reached". We don't care if that file exists 37 | # or not on PHP 7. 38 | - phpenv config-rm xdebug.ini || true 39 | 40 | # Update composer. 41 | - composer --verbose self-update 42 | 43 | # Create database. 44 | - mysql -e 'create database myproject' 45 | 46 | install: 47 | # Install Composer dependencies. 48 | - composer --verbose install 49 | 50 | # Define build properties for the local test environment. 51 | - echo -e "drupal.base_url = http://localhost:8888\n" > build.properties.local 52 | 53 | # Build the project. 54 | - ./vendor/bin/phing build-dev 55 | 56 | # Install Drupal. This is not needed when doing a coding standards check. 57 | - test ${TEST} == "PHP_CodeSniffer" || ./vendor/bin/phing install-dev 58 | 59 | # Start a web server on port 8888 in the background. 60 | - cd $TRAVIS_BUILD_DIR/web 61 | - nohup php -S localhost:8888 > /dev/null 2>&1 & 62 | 63 | # Wait until the web server is responding. 64 | - until curl -s localhost:8888; do true; done > /dev/null 65 | - cd $TRAVIS_BUILD_DIR 66 | 67 | script: ./scripts/travis-ci/run-test.sh ${TEST} 68 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | {description} 294 | Copyright (C) {year} {fullname} 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | {signature of Ty Coon}, 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Composer template for Drupal projects 2 | 3 | [![Build Status](https://travis-ci.org/drupal-composer/drupal-project.svg?branch=8.x)](https://travis-ci.org/drupal-composer/drupal-project) 4 | 5 | This project template provides a starter kit for managing your site 6 | dependencies with [Composer](https://getcomposer.org/). 7 | 8 | This is a fork of [drupal-composer/drupal-project](https://github.com/drupal-composer/drupal-project) 9 | which is set up with support for Continuous Integration out of the box. 10 | 11 | ## Additional features 12 | 13 | This fork has the following additions to the original version of drupal-project: 14 | 15 | * **Customizable builds**: Different builds for production and development 16 | environments are created with [Phing](https://www.phing.info/). These can be 17 | fully customized using a set of configuration files. 18 | * **Behat support**: Behat is included so you can easily test user scenarios. 19 | * **Preconfigured test suites**: PHPUnit is fully set up for running unit tests 20 | as well as kernel tests, web tests and javascript tests. 21 | * **PHP CodeSniffer**: Check compliance with coding standards with a single 22 | command, or set it up to scan automatically whenever you `git push`. 23 | * **Travis CI**: Integrates seamlessly with [Travis CI](https://travis-ci.com/) 24 | so you can automate your tests right from the start. No setup needed! 25 | 26 | If you want to know how to use it as replacement for 27 | [Drush Make](https://github.com/drush-ops/drush/blob/8.x/docs/make.md) visit 28 | the [Documentation on drupal.org](https://www.drupal.org/node/2471553). 29 | 30 | ## Usage 31 | 32 | First you need to [install composer](https://getcomposer.org/doc/00-intro.md#installation-linux-unix-osx). 33 | 34 | > Note: The instructions below refer to the [global composer installation](https://getcomposer.org/doc/00-intro.md#globally). 35 | You might need to replace `composer` with `php composer.phar` (or similar) 36 | for your setup. 37 | 38 | After that you can create the project: 39 | 40 | ``` 41 | git clone https://github.com/pfrenssen/drupal-project.git some-dir 42 | cd some-dir 43 | composer install 44 | ``` 45 | 46 | With `composer require ...` you can download new dependencies to your 47 | installation. 48 | 49 | ``` 50 | composer require drupal/devel:~1.0 51 | ``` 52 | 53 | The `composer create-project` command passes ownership of all files to the 54 | project that is created. You should create a new git repository, and commit 55 | all files not excluded by the .gitignore file. 56 | 57 | ## What does the template do? 58 | 59 | When installing the given `composer.json` some tasks are taken care of: 60 | 61 | * Drupal will be installed in the `web`-directory. 62 | * Autoloader is implemented to use the generated composer autoloader in `vendor/autoload.php`, 63 | instead of the one provided by Drupal (`web/vendor/autoload.php`). 64 | * Modules (packages of type `drupal-module`) will be placed in `web/modules/contrib/` 65 | * Theme (packages of type `drupal-theme`) will be placed in `web/themes/contrib/` 66 | * Profiles (packages of type `drupal-profile`) will be placed in `web/profiles/contrib/` 67 | * Creates default writable versions of `settings.php` and `services.yml`. 68 | * Creates `web/sites/default/files`-directory. 69 | * Latest version of drush is installed locally for use at `vendor/bin/drush`. 70 | * Latest version of DrupalConsole is installed locally for use at `vendor/bin/drupal`. 71 | * Creates environment variables based on your .env file. See [.env.example](.env.example). 72 | 73 | ## Updating Drupal Core 74 | 75 | This project will attempt to keep all of your Drupal Core files up-to-date; the 76 | project [drupal-composer/drupal-scaffold](https://github.com/drupal-composer/drupal-scaffold) 77 | is used to ensure that your scaffold files are updated every time drupal/core is 78 | updated. If you customize any of the "scaffolding" files (commonly .htaccess), 79 | you may need to merge conflicts if any of your modified files are updated in a 80 | new release of Drupal core. 81 | 82 | Follow the steps below to update your core files. 83 | 84 | 1. Run `composer update drupal/core webflo/drupal-core-require-dev "symfony/*" --with-dependencies` to update Drupal Core and its dependencies. 85 | 1. Run `git diff` to determine if any of the scaffolding files have changed. 86 | Review the files for any changes and restore any customizations to 87 | `.htaccess` or `robots.txt`. 88 | 1. Commit everything all together in a single commit, so `web` will remain in 89 | sync with the `core` when checking out branches or running `git bisect`. 90 | 1. In the event that there are non-trivial conflicts in step 2, you may wish 91 | to perform these steps on a branch, and use `git merge` to combine the 92 | updated core files with your customized files. This facilitates the use 93 | of a [three-way merge tool such as kdiff3](http://www.gitshah.com/2010/12/how-to-setup-kdiff-as-diff-tool-for-git.html). This setup is not necessary if your changes are simple; 94 | keeping all of your modifications at the beginning or end of the file is a 95 | good strategy to keep merges easy. 96 | 97 | ## Generate composer.json from existing project 98 | 99 | With using [the "Composer Generate" drush extension](https://www.drupal.org/project/composer_generate) 100 | you can now generate a basic `composer.json` file from an existing project. Note 101 | that the generated `composer.json` might differ from this project's file. 102 | 103 | 104 | ## Customize build properties 105 | 106 | Create a new file in the root of the project named `build.properties.local` 107 | using your favourite text editor: 108 | 109 | ``` 110 | $ vim build.properties.local 111 | ``` 112 | 113 | This file will contain configuration which is unique to your development 114 | machine. This is mainly useful for specifying your database credentials and the 115 | username and password of the Drupal admin user so they can be used during the 116 | installation. 117 | 118 | Because these settings are personal they should not be shared with the rest of 119 | the team. Make sure you never commit this file! 120 | 121 | All options you can use can be found in the `build.properties.dist` file. Just 122 | copy the lines you want to override and change their values. For example: 123 | 124 | ``` 125 | # Database settings. 126 | drupal.db.name = my_database 127 | drupal.db.user = root 128 | drupal.db.password = hunter2 129 | 130 | # Admin user. 131 | drupal.admin.username = admin 132 | drupal.admin.password = admin 133 | 134 | # The base URL to use in tests. 135 | drupal.base_url = http://myproject.local 136 | 137 | # Verbosity of Drush commands. Set to 'yes' for verbose output. 138 | drush.verbose = yes 139 | ``` 140 | 141 | 142 | ## Listing the available build commands 143 | 144 | You can get a list of all the available Phing build commands ("targets") with a 145 | short description of each target with the following command: 146 | 147 | ``` 148 | $ ./vendor/bin/phing 149 | ``` 150 | 151 | 152 | ## Install the website. 153 | 154 | ``` 155 | $ ./vendor/bin/phing install 156 | ``` 157 | 158 | 159 | ## Set up tools for the development environment 160 | 161 | If you want to install a version suitable for development you can execute the 162 | `setup-dev` Phing target. 163 | 164 | ``` 165 | $ ./vendor/bin/phing setup-dev 166 | ``` 167 | 168 | This will perform the following tasks: 169 | 170 | 1. Configure Behat. 171 | 2. Configure PHP CodeSniffer. 172 | 3. Enable 'development mode'. This will: 173 | * Enable the services in `development.services.yml`. 174 | * Show all error messages with backtrace information. 175 | * Disable CSS and JS aggregation. 176 | * Disable the render cache. 177 | * Allow test modules and themes to be installed. 178 | * Enable access to `rebuild.php`. 179 | 4. Enable development modules. 180 | 5. Create a demo user for each user role. 181 | 182 | To set up a development environment quickly, you can perform both the `install` 183 | and `setup-dev` targets at once by executing `install-dev`: 184 | 185 | ``` 186 | $ ./vendor/bin/phing install-dev 187 | ``` 188 | 189 | 190 | ## Running Behat tests 191 | 192 | The Behat test suite is located in the `tests/` folder. The easiest way to run 193 | them is by going into this folder and executing the following command: 194 | 195 | ``` 196 | $ cd tests/ 197 | $ ./behat 198 | ``` 199 | 200 | If you want to execute a single test, just provide the path to the test as an 201 | argument. The tests are located in `tests/features/`: 202 | 203 | ``` 204 | $ cd tests/ 205 | $ ./behat features/authentication.feature 206 | ``` 207 | 208 | If you want to run the tests from a different folder, then provide the path to 209 | `tests/behat.yml` with the `-c` option: 210 | 211 | ``` 212 | # Run the tests from the root folder of the project. 213 | $ ./vendor/bin/behat -c tests/behat.yml 214 | ``` 215 | 216 | ### Testing JavaScript functionality 217 | 218 | For testing functionality written in JavaScript you can use software such as 219 | Selenium and PhantomJS to run Behat tests in a real browser. 220 | 221 | To enable JavaScript testing for a Behat scenario, add the `@javascript` tag to 222 | the test, and make sure the WebDriver of your choice is running in the 223 | background. 224 | 225 | #### Chrome 226 | 227 | Set the following build property in your `build.properties` file: 228 | 229 | ``` 230 | # The browser to use for testing, either 'firefox' or 'chrome'. 231 | behat.browser_name = chrome 232 | ``` 233 | 234 | Make sure to regenerate your Behat configuration file after making this change: 235 | 236 | ``` 237 | $ ./vendor/bin/phing setup-behat 238 | ``` 239 | 240 | You can install Selenium and Chrome / Chromium locally on your system and start 241 | Selenium with `java -jar selenium-server-standalone.jar`. 242 | 243 | However the simplest way of running Chrome on Selenium is using a container. 244 | You can use Docker to install and run it with a single command. This will 245 | download all necessary files and start it in the background in headless mode: 246 | 247 | ``` 248 | $ docker run -d -p 4444:4444 --network=host selenium/standalone-chrome 249 | ``` 250 | 251 | If you want to look at the browser running, you can run the 'debug' version and 252 | use a VNC client such as TigerVNC to connect to the browser (the password is 253 | 'secret'): 254 | 255 | ``` 256 | $ docker run -d -p 4444:4444 -p 5900:5900 --network=host selenium/standalone-chrome-debug 257 | ``` 258 | 259 | For more information on running a containerized Selenium, see 260 | [SeleniumHQ/docker-selenium](https://github.com/SeleniumHQ/docker-selenium). 261 | 262 | 263 | #### Firefox 264 | 265 | Set the following build property in your `build.properties` file: 266 | 267 | ``` 268 | # The browser to use for testing, either 'firefox' or 'chrome'. 269 | behat.browser_name = firefox 270 | ``` 271 | Make sure to regenerate your Behat configuration file after making this change: 272 | 273 | ``` 274 | $ ./vendor/bin/phing setup-behat 275 | ``` 276 | 277 | You can install Selenium and Firefox locally on your system and start Selenium 278 | with `java -jar selenium-server-standalone.jar`. 279 | 280 | However the simplest way of running Firefox on Selenium is using a container. 281 | You can use Docker to install and run it with a single command. This will 282 | download all necessary files and start it in the background in headless mode: 283 | 284 | 285 | ``` 286 | $ docker run -d -p 4444:4444 --network=host selenium/standalone-firefox:2.53.1 287 | ``` 288 | 289 | Note that the WebDriver implementation for Firefox 290 | ([Marionette](https://developer.mozilla.org/en-US/docs/Mozilla/QA/Marionette)) 291 | is not yet complete for Selenium 3, so we are specifying the latest 2.x 292 | version. This will probably be ready soon, when Selenium 3 is out of beta. 293 | 294 | If you want to look at the browser running, you can run the 'debug' version and 295 | use a VNC client such as TigerVNC to connect to the browser (the password is 296 | 'secret'): 297 | 298 | ``` 299 | $ docker run -d -p 4444:4444 -p 5900:5900 --network=host selenium/standalone-firefox-debug:2.53.1 300 | ``` 301 | 302 | For more information on running a containerized Selenium, see 303 | [SeleniumHQ/docker-selenium](https://github.com/SeleniumHQ/docker-selenium). 304 | 305 | 306 | #### PhantomJS 307 | 308 | PhantomJS is a headless browser based on Webkit, which is the same engine which 309 | Chrome uses. You can install it from your package manager, or [download 310 | it](http://phantomjs.org/download.html). 311 | 312 | Declare that you are using a Chrome-alike browser in your `build.properties` 313 | file: 314 | 315 | 316 | ``` 317 | # The browser to use for testing, either 'firefox' or 'chrome'. 318 | behat.browser_name = chrome 319 | ``` 320 | 321 | Make sure to regenerate your Behat configuration file after making this change: 322 | 323 | ``` 324 | $ ./vendor/bin/phing setup-behat 325 | ``` 326 | 327 | Now you can run PhantomJS in WebDriver mode, and run your tests. All your tests 328 | marked with `@javascript` will now be executed using PhantomJS. 329 | 330 | ``` 331 | $ phantomjs --webdriver=4444 332 | ``` 333 | 334 | 335 | ## Running PHPUnit tests 336 | 337 | Run the tests from the `web` folder: 338 | 339 | ``` 340 | $ cd web/ 341 | $ ../vendor/bin/phpunit 342 | ``` 343 | 344 | By default all tests in the folders `web/modules/custom`, `web/profiles` and 345 | `web/themes/custom` are included when running the tests. Check the section on 346 | PHPUnit in the `build.properties.dist` to customize the tests. 347 | 348 | 349 | ## Checking for coding standards violations 350 | 351 | ### Set up PHP CodeSniffer 352 | 353 | PHP CodeSniffer is included to do coding standards checks of PHP and JS files. 354 | In the default configuration it will scan all files in the following folders: 355 | - `web/modules` (excluding `web/modules/contrib`) 356 | - `web/profiles` 357 | - `web/themes` 358 | 359 | First you'll need to execute the `setup-php-codesniffer` Phing target (note that 360 | this also runs as part of the `install-dev` and `setup-dev` targets): 361 | 362 | ``` 363 | $ ./vendor/bin/phing setup-php-codesniffer 364 | ``` 365 | 366 | This will generate a `phpcs.xml` file containing settings specific to your local 367 | environment. Make sure to never commit this file. 368 | 369 | ### Run coding standards checks 370 | 371 | #### Run checks manually 372 | 373 | The coding standards checks can then be run as follows: 374 | 375 | ``` 376 | # Scan all files for coding standards violations. 377 | $ ./vendor/bin/phpcs 378 | 379 | # Scan only a single folder. 380 | $ ./vendor/bin/phpcs web/modules/custom/mymodule 381 | ``` 382 | 383 | #### Run checks automatically when pushing 384 | 385 | To save yourself the embarrassment of pushing non-compliant code to the git 386 | repository you can put the following line in your `build.properties.local`: 387 | 388 | ``` 389 | # Whether or not to run a coding standards check before doing a git push. Note 390 | # that this will abort the push if the coding standards check fails. 391 | phpcs.prepush.enable = 1 392 | ``` 393 | 394 | and then regenerate your PHP CodeSniffer configuration: 395 | 396 | ``` 397 | $ ./vendor/bin/phing setup-php-codesniffer 398 | ``` 399 | 400 | If your project requires all team members to follow coding standards, put this 401 | line in the project configuration (`build.properties`) instead. 402 | 403 | Note that this will not allow you to push any code that fails the coding 404 | standards check. If you really need to push in a hurry, then you can disable 405 | the coding standards check by executing this Phing target: 406 | 407 | ``` 408 | $ ./vendor/bin/phing disable-pre-push 409 | ``` 410 | 411 | The pre-push hook will be reinstated when the `setup-php-codesniffer` target 412 | is executed. 413 | 414 | 415 | ### Customize configuration 416 | 417 | The basic configuration can be changed by copying the relevant Phing properties 418 | from the "PHP CodeSniffer configuration" section in `build.properties.dist` to 419 | `build.properties` and changing them to your requirements. Then regenerate the 420 | `phpcs.xml` file by running the `setup-php-codesniffer` target: 421 | 422 | ``` 423 | $ ./vendor/bin/phing setup-php-codesniffer 424 | ``` 425 | 426 | To change to PHP CodeSniffer ruleset itself, make a copy of the file 427 | `phpcs-ruleset.xml.dist` and rename it to `phpcs-ruleset.xml`, and then put this 428 | line in your `build.properties` file: 429 | 430 | ``` 431 | phpcs.standard = ${project.basedir}/phpcs-ruleset.xml 432 | ``` 433 | 434 | For more information on configuring the ruleset see [Annotated ruleset](http://pear.php.net/manual/en/package.php.php-codesniffer.annotated-ruleset.php). 435 | 436 | 437 | ## FAQ 438 | 439 | ### Should I commit the contrib modules I download? 440 | 441 | Composer recommends **no**. They provide [argumentation against but also 442 | workrounds if a project decides to do it anyway](https://getcomposer.org/doc/faqs/should-i-commit-the-dependencies-in-my-vendor-directory.md). 443 | 444 | ### Should I commit the scaffolding files? 445 | 446 | The [drupal-scaffold](https://github.com/drupal-composer/drupal-scaffold) plugin can download the scaffold files (like 447 | index.php, update.php, …) to the web/ directory of your project. If you have not customized those files you could choose 448 | to not check them into your version control system (e.g. git). If that is the case for your project it might be 449 | convenient to automatically run the drupal-scaffold plugin after every install or update of your project. You can 450 | achieve that by registering `@composer drupal:scaffold` as post-install and post-update command in your composer.json: 451 | 452 | ```json 453 | "scripts": { 454 | "post-install-cmd": [ 455 | "@composer drupal:scaffold", 456 | "..." 457 | ], 458 | "post-update-cmd": [ 459 | "@composer drupal:scaffold", 460 | "..." 461 | ] 462 | }, 463 | ``` 464 | ### How can I apply patches to downloaded modules? 465 | 466 | If you need to apply patches (depending on the project being modified, a pull 467 | request is often a better solution), you can do so with the 468 | [composer-patches](https://github.com/cweagans/composer-patches) plugin. 469 | 470 | To add a patch to drupal module foobar insert the patches section in the extra 471 | section of composer.json: 472 | ```json 473 | "extra": { 474 | "patches": { 475 | "drupal/foobar": { 476 | "Patch description": "URL or local path to patch" 477 | } 478 | } 479 | } 480 | ``` 481 | ### How do I switch from packagist.drupal-composer.org to packages.drupal.org? 482 | 483 | Follow the instructions in the [documentation on drupal.org](https://www.drupal.org/docs/develop/using-composer/using-packagesdrupalorg). 484 | 485 | ### How do I specify a PHP version ? 486 | 487 | This project supports PHP 5.6 as minimum version (see [Drupal 8 PHP requirements](https://www.drupal.org/docs/8/system-requirements/drupal-8-php-requirements)), however it's possible that a `composer update` will upgrade some package that will then require PHP 7+. 488 | 489 | To prevent this you can add this code to specify the PHP version you want to use in the `config` section of `composer.json`: 490 | ```json 491 | "config": { 492 | "sort-packages": true, 493 | "platform": { 494 | "php": "5.6.40" 495 | } 496 | }, 497 | ``` 498 | -------------------------------------------------------------------------------- /build.dist.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 45 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 161 | 165 | 166 | 167 | 168 | 169 | 174 | 178 | 183 | ${drupal.demo.user} 184 | ${drupal.demo.user} 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 241 | ${module} 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 270 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 286 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 324 | 325 | 329 | 330 | 334 | 335 | 339 | 340 | 344 | 345 | 346 | -------------------------------------------------------------------------------- /build.properties.dist: -------------------------------------------------------------------------------- 1 | # Drupal configuration 2 | # -------------------- 3 | 4 | # The project name. 5 | project.name = myproject 6 | 7 | # The install profile to use. 8 | website.profile.name = standard 9 | 10 | # The site name. 11 | website.site.name = MyDrupalProject 12 | 13 | # Database settings. 14 | drupal.db.type = mysql 15 | drupal.db.name = ${project.name} 16 | drupal.db.user = root 17 | drupal.db.password = 18 | drupal.db.host = 127.0.0.1 19 | drupal.db.port = 3306 20 | drupal.db.url = ${drupal.db.type}://${drupal.db.user}:${drupal.db.password}@${drupal.db.host}:${drupal.db.port}/${drupal.db.name} 21 | 22 | # Admin user. 23 | drupal.admin.username = admin 24 | drupal.admin.password = admin 25 | drupal.admin.email = admin@example.com 26 | 27 | # Comma-separated list of demo users to create. The username and password will 28 | # be taken from the role. A normal authenticated user with username and password 29 | # 'user' will also be created. 30 | drupal.demo.users = administrator 31 | 32 | # The base URL. This is used for doing functional tests in Behat and PHPUnit. 33 | drupal.base_url = http://localhost 34 | 35 | 36 | # Paths 37 | # ----- 38 | 39 | # Paths to executables. 40 | composer.bin = ${project.basedir}/vendor/bin/composer 41 | drush.bin = ${project.basedir}/vendor/bin/drush 42 | phing.bin = ${project.basedir}/vendor/bin/phing 43 | 44 | # Files and directories inside the Drupal installation. 45 | website.drupal.dir = ${project.basedir}/web 46 | 47 | website.sites.dir = ${website.drupal.dir}/sites 48 | website.site.dir = ${website.drupal.sites.dir}/all 49 | 50 | website.settings.dir = ${website.sites.dir}/default 51 | website.settings.php = ${website.settings.dir}/settings.php 52 | website.settings.local.php.example = ${website.sites.dir}/example.settings.local.php 53 | website.settings.local.php = ${website.settings.dir}/settings.local.php 54 | website.services.yml = ${website.settings.dir}/services.yml 55 | website.services.yml.default = ${website.settings.dir}/default.services.yml 56 | website.files.dir = ${website.settings.dir}/files 57 | 58 | website.modules.dir = ${website.drupal.dir}/modules 59 | website.profiles.dir = ${website.drupal.dir}/profiles 60 | website.themes.dir = ${website.drupal.dir}/themes 61 | 62 | 63 | # Behat configuration 64 | # ------------------- 65 | 66 | # The location of the Behat executable. 67 | behat.bin = ${project.basedir}/vendor/bin/behat 68 | 69 | # The location of the Behat tests. 70 | behat.dir = ${project.basedir}/tests 71 | 72 | # The location of the Behat configuration template. 73 | behat.yml.template = ${behat.dir}/behat.yml.dist 74 | 75 | # The location of the generated Behat configuration file. 76 | behat.yml.path = ${behat.dir}/behat.yml 77 | 78 | # The base URL to use in Behat tests. 79 | behat.base_url = ${drupal.base_url} 80 | 81 | # The browser to use for testing, either 'firefox' or 'chrome'. 82 | behat.browser_name = chrome 83 | 84 | # The port of the webdriver host (e.g. Selenium or PhantomJS). 85 | behat.webdriver_port = 4444 86 | 87 | # The URL of the webdriver host (e.g. Selenium or PhantomJS). 88 | behat.webdriver_url = http://localhost:${behat.webdriver_port}/wd/hub 89 | 90 | # The location to search for Behat subcontexts. 91 | behat.subcontexts.path = ${website.modules.dir} 92 | 93 | # The output format to use for Behat tests, either 'progress' or 'pretty'. 94 | behat.formatter.name = progress 95 | 96 | # The location of the test files. 97 | behat.files.path = ${behat.dir}/fixtures/files 98 | 99 | 100 | # PHP CodeSniffer configuration 101 | # ----------------------------- 102 | 103 | # The file extensions to test. 104 | phpcs.extensions = php inc module install info test profile theme css js 105 | 106 | # The default configuration file to generate. 107 | phpcs.config = ${project.basedir}/phpcs.xml 108 | 109 | # The coding standard to use. If you want to customize the rules, make a copy of 110 | # the file and name it 'phpcs-ruleset.xml'. Then copy this property to your 111 | # build.properties file and remove the '.dist' suffix. 112 | phpcs.standard = ${project.basedir}/phpcs-ruleset.xml.dist 113 | 114 | # Paths to check, delimited by semicolons. 115 | phpcs.files = ${website.modules.dir};${website.profiles.dir};${website.themes.dir} 116 | 117 | # Paths to ignore, delimited by semicolons. 118 | phpcs.ignore = ${website.modules.dir}/contrib 119 | 120 | # The report format. For example 'full', 'summary', 'diff', 'xml', 'json'. 121 | phpcs.report = full 122 | 123 | # Whether or not to show sniff codes in the report. 124 | phpcs.sniffcodes = 0 125 | 126 | # Whether or not to show the progress of the run. 127 | phpcs.progress = 1 128 | 129 | # The location of the file containing the global configuration options. 130 | phpcs.global.config = ${project.basedir}/vendor/squizlabs/php_codesniffer/CodeSniffer.conf 131 | 132 | # Whether or not to run a coding standards check before doing a git push. Note 133 | # that this will abort the push if the coding standards check fails. 134 | phpcs.prepush.enable = 0 135 | 136 | # The source and destination paths of the git pre-push hook. 137 | phpcs.prepush.source = ${project.basedir}/vendor/pfrenssen/phpcs-pre-push/pre-push 138 | phpcs.prepush.destination = ${project.basedir}/.git/hooks/pre-push 139 | 140 | 141 | # PHPUnit configuration 142 | # --------------------- 143 | 144 | # The path to the configuration file template. 145 | phpunit.dist = ${project.basedir}/phpunit.xml.dist 146 | 147 | # The path to the generated configuration file. This is typically inside the web 148 | # root. 149 | phpunit.config = ${website.drupal.dir}/phpunit.xml 150 | 151 | # The name to give to the test suite of the project. 152 | phpunit.testsuite.name = ${project.name} 153 | 154 | # Comma-separated list of directories containing tests to execute. These are 155 | # relative to the Drupal root. 156 | phpunit.directories = ./modules/custom,./profiles,./themes/custom 157 | 158 | # Comma-separated list of test files to execute. These are relative to the 159 | # Drupal root. 160 | phpunit.files = 161 | 162 | # The base URL to use in functional tests. 163 | phpunit.base_url = ${drupal.base_url} 164 | 165 | # The database URL to use in kernel tests and functional tests. 166 | phpunit.db_url = ${drupal.db.url} 167 | 168 | # The path to the directory where HTML output from browsertests is stored. 169 | phpunit.browsertest_output_dir = ${website.sites.dir}/simpletest/browser_output 170 | 171 | # The path to the file that lists HTML output from browsertests. 172 | phpunit.browsertest_output_file = ${phpunit.browsertest_output_dir}/outputfile.txt 173 | 174 | 175 | # Drush configuration 176 | # ------------------- 177 | 178 | # The location of the Drush configuration folder. 179 | drush.dir = ${project.basedir}/drush 180 | 181 | # The location of the Drush configuration template. 182 | drush.yml.template = ${drush.dir}/drush.yml.dist 183 | 184 | # The location of the generated Drush configuration file. 185 | drush.yml.path = ${drush.dir}/drush.yml 186 | 187 | # Verbosity of Drush commands. Set to 'true' for verbose output. 188 | drush.verbose = false 189 | 190 | 191 | # Development options 192 | # ------------------- 193 | 194 | # Development modules to enable. 195 | drupal.modules.dev = config_update devel field_ui menu_ui views_ui 196 | 197 | # Redirect outgoing e-mail to disk. This prevents e-mail from being 198 | # accidentally sent out on development machines. The mails can be found in the 199 | # folder temporary://devel-mails. This requires the devel module to be enabled. 200 | drupal.redirect.email = yes 201 | -------------------------------------------------------------------------------- /build.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pfrenssen/drupal-project", 3 | "description": "Project template for Drupal 8 projects with Composer, Phing and Behat", 4 | "type": "project", 5 | "license": "GPL-2.0-or-later", 6 | "authors": [ 7 | { 8 | "name": "", 9 | "role": "" 10 | } 11 | ], 12 | "repositories": [ 13 | { 14 | "type": "composer", 15 | "url": "https://packages.drupal.org/8" 16 | }, 17 | { 18 | "type": "package", 19 | "package": { 20 | "name": "pfrenssen/phpcs-pre-push", 21 | "version": "1.0", 22 | "type": "git-hook", 23 | "source": { 24 | "url": "https://gist.github.com/498fc52fea3f965f6640.git", 25 | "type": "git", 26 | "reference": "master" 27 | } 28 | } 29 | }, 30 | { 31 | "type": "vcs", 32 | "url": "https://git.drupal.org/project/phingdrushtask.git" 33 | } 34 | ], 35 | "require": { 36 | "php": ">=5.6", 37 | "composer/composer": "~1.0", 38 | "composer/installers": "^1.2", 39 | "cweagans/composer-patches": "^1.6.5", 40 | "drupal-composer/drupal-scaffold": "^2.5", 41 | "drupal/console": "^1.0.2", 42 | "drupal/core": "^8.7.0", 43 | "drush/drush": "^9.0.0", 44 | "vlucas/phpdotenv": "^2.4", 45 | "webflo/drupal-finder": "^1.0.0", 46 | "webmozart/path-util": "^2.3", 47 | "zaporylie/composer-drupal-optimizations": "^1.0" 48 | }, 49 | "require-dev": { 50 | "behat/mink": "~1.7", 51 | "behat/mink-goutte-driver": "~1.2", 52 | "behat/mink-selenium2-driver": "dev-master#721cbbaf37f71434f6acb604dc3a17a6a9be0c4c", 53 | "drupal/coder": "~8.2.7", 54 | "drupal/config_update": "1.*@dev", 55 | "drupal/devel": "1.*@dev", 56 | "drupal/drupal-extension": "~3.0", 57 | "drupal/phingdrushtask": "dev-7.x-2.x", 58 | "jcalderonzumba/gastonjs": "^1.1@dev", 59 | "jcalderonzumba/mink-phantomjs-driver": "~0.3.1", 60 | "mikey179/vfsStream": "~1.2", 61 | "pfrenssen/phpcs-pre-push": "1.0", 62 | "phing/phing": "~2.10", 63 | "phpunit/phpunit": ">=4.8.28 <5", 64 | "symfony/css-selector": "~2.8|~3.0|~4.0", 65 | "webflo/drupal-core-require-dev": "^8.7.0" 66 | }, 67 | "conflict": { 68 | "drupal/drupal": "*" 69 | }, 70 | "minimum-stability": "dev", 71 | "prefer-stable": true, 72 | "config": { 73 | "sort-packages": true, 74 | "platform": { 75 | "php": "5.6.31" 76 | } 77 | }, 78 | "autoload": { 79 | "psr-4": { 80 | "Drupal\\DrupalProject\\": "tests/src", 81 | "DrupalProject\\": "src" 82 | }, 83 | "classmap": [ 84 | "scripts/composer/ScriptHandler.php" 85 | ], 86 | "files": ["load.environment.php"] 87 | }, 88 | "scripts": { 89 | "pre-install-cmd": [ 90 | "DrupalProject\\composer\\ScriptHandler::checkComposerVersion" 91 | ], 92 | "pre-update-cmd": [ 93 | "DrupalProject\\composer\\ScriptHandler::checkComposerVersion" 94 | ], 95 | "post-install-cmd": [ 96 | "DrupalProject\\composer\\ScriptHandler::createRequiredFiles" 97 | ], 98 | "post-update-cmd": [ 99 | "DrupalProject\\composer\\ScriptHandler::createRequiredFiles" 100 | ] 101 | }, 102 | "extra": { 103 | "composer-exit-on-patch-failure": true, 104 | "patchLevel": { 105 | "drupal/core": "-p2" 106 | }, 107 | "installer-paths": { 108 | "web/core": ["type:drupal-core"], 109 | "web/libraries/{$name}": ["type:drupal-library"], 110 | "web/modules/contrib/{$name}": ["type:drupal-module"], 111 | "web/profiles/contrib/{$name}": ["type:drupal-profile"], 112 | "web/themes/contrib/{$name}": ["type:drupal-theme"], 113 | "drush/Commands/{$name}": ["type:drupal-drush"] 114 | }, 115 | "drupal-scaffold": { 116 | "initial": { 117 | ".editorconfig": "../.editorconfig", 118 | ".gitattributes": "../.gitattributes" 119 | } 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /drush/Commands/PolicyCommands.php: -------------------------------------------------------------------------------- 1 | input()->getArgument('target') == '@prod') { 22 | throw new \Exception(dt('Per !file, you may never overwrite the production database.', ['!file' => __FILE__])); 23 | } 24 | } 25 | 26 | /** 27 | * Limit rsync operations to production site. 28 | * 29 | * @hook validate core:rsync 30 | * 31 | * @throws \Exception 32 | */ 33 | public function rsyncValidate(CommandData $commandData) { 34 | if (preg_match("/^@prod/", $commandData->input()->getArgument('target'))) { 35 | throw new \Exception(dt('Per !file, you may never rsync to the production site.', ['!file' => __FILE__])); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /drush/README.md: -------------------------------------------------------------------------------- 1 | This directory contains commands, configuration and site aliases for Drush. See https://packagist.org/search/?type=drupal-drush for a directory of Drush commands installable via Composer. 2 | -------------------------------------------------------------------------------- /drush/drush.yml.dist: -------------------------------------------------------------------------------- 1 | # The Drush configuration file 2 | # 3 | # Docs at https://github.com/drush-ops/drush/blob/master/examples/example.drush.yml 4 | 5 | options: 6 | uri: '${drupal.base_url}' 7 | verbose: ${drush.verbose} 8 | -------------------------------------------------------------------------------- /drush/sites/self.site.yml: -------------------------------------------------------------------------------- 1 | # Edit or remove this file as needed. 2 | # Docs at https://github.com/drush-ops/drush/blob/master/examples/example.site.yml 3 | 4 | #prod: 5 | # host: prod.domain.com 6 | # user: www-admin 7 | # root: /path/to/drupal 8 | # uri: http://www.example.com 9 | # 10 | #stage: 11 | # host: stage.domain.com 12 | # user: www-admin 13 | # root: /path/to/drupal 14 | # uri: http://stage.example.com 15 | -------------------------------------------------------------------------------- /load.environment.php: -------------------------------------------------------------------------------- 1 | load(); 17 | } 18 | catch (InvalidPathException $e) { 19 | // Do nothing. Production environments rarely use .env files. 20 | } 21 | -------------------------------------------------------------------------------- /phpcs-ruleset.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Drupal coding standard 6 | 7 | 8 | *.gif 9 | *.less 10 | *.png 11 | 12 | 13 | *.min.css 14 | *.min.js 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 12 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | ./core/includes 41 | ./core/lib 42 | ./core/modules 43 | ./modules 44 | ./sites 45 | 46 | 47 | ./ 48 | ./ 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /scripts/composer/ScriptHandler.php: -------------------------------------------------------------------------------- 1 | locateRoot(getcwd()); 22 | $drupalRoot = $drupalFinder->getDrupalRoot(); 23 | 24 | $dirs = [ 25 | 'modules', 26 | 'profiles', 27 | 'themes', 28 | ]; 29 | 30 | // Required for unit testing 31 | foreach ($dirs as $dir) { 32 | if (!$fs->exists($drupalRoot . '/'. $dir)) { 33 | $fs->mkdir($drupalRoot . '/'. $dir); 34 | $fs->touch($drupalRoot . '/'. $dir . '/.gitkeep'); 35 | } 36 | } 37 | 38 | // Prepare the settings file for installation 39 | if (!$fs->exists($drupalRoot . '/sites/default/settings.php') and $fs->exists($drupalRoot . '/sites/default/default.settings.php')) { 40 | $fs->copy($drupalRoot . '/sites/default/default.settings.php', $drupalRoot . '/sites/default/settings.php'); 41 | require_once $drupalRoot . '/core/includes/bootstrap.inc'; 42 | require_once $drupalRoot . '/core/includes/install.inc'; 43 | $settings['config_directories'] = [ 44 | CONFIG_SYNC_DIRECTORY => (object) [ 45 | 'value' => Path::makeRelative($drupalFinder->getComposerRoot() . '/config/sync', $drupalRoot), 46 | 'required' => TRUE, 47 | ], 48 | ]; 49 | drupal_rewrite_settings($settings, $drupalRoot . '/sites/default/settings.php'); 50 | $fs->chmod($drupalRoot . '/sites/default/settings.php', 0640); 51 | $event->getIO()->write("Created a sites/default/settings.php file with chmod 0640"); 52 | } 53 | 54 | // Create the files directory with chmod 0755 55 | if (!$fs->exists($drupalRoot . '/sites/default/files')) { 56 | $oldmask = umask(0); 57 | $fs->mkdir($drupalRoot . '/sites/default/files', 0755); 58 | umask($oldmask); 59 | $event->getIO()->write("Created a sites/default/files directory with chmod 0755"); 60 | } 61 | } 62 | 63 | /** 64 | * Checks if the installed version of Composer is compatible. 65 | * 66 | * Composer 1.0.0 and higher consider a `composer install` without having a 67 | * lock file present as equal to `composer update`. We do not ship with a lock 68 | * file to avoid merge conflicts downstream, meaning that if a project is 69 | * installed with an older version of Composer the scaffolding of Drupal will 70 | * not be triggered. We check this here instead of in drupal-scaffold to be 71 | * able to give immediate feedback to the end user, rather than failing the 72 | * installation after going through the lengthy process of compiling and 73 | * downloading the Composer dependencies. 74 | * 75 | * @see https://github.com/composer/composer/pull/5035 76 | */ 77 | public static function checkComposerVersion(Event $event) { 78 | $composer = $event->getComposer(); 79 | $io = $event->getIO(); 80 | 81 | $version = $composer::VERSION; 82 | 83 | // The dev-channel of composer uses the git revision as version number, 84 | // try to the branch alias instead. 85 | if (preg_match('/^[0-9a-f]{40}$/i', $version)) { 86 | $version = $composer::BRANCH_ALIAS_VERSION; 87 | } 88 | 89 | // If Composer is installed through git we have no easy way to determine if 90 | // it is new enough, just display a warning. 91 | if ($version === '@package_version@' || $version === '@package_branch_alias_version@') { 92 | $io->writeError('You are running a development version of Composer. If you experience problems, please update Composer to the latest stable version.'); 93 | } 94 | elseif (Comparator::lessThan($version, '1.0.0')) { 95 | $io->writeError('Drupal-project requires Composer version 1.0.0 or higher. Please update your Composer before continuing.'); 96 | exit(1); 97 | } 98 | } 99 | 100 | } 101 | -------------------------------------------------------------------------------- /scripts/travis-ci/run-test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | case "$1" in 4 | PHP_CodeSniffer) 5 | cd $TRAVIS_BUILD_DIR 6 | ./vendor/bin/phpcs 7 | exit $? 8 | ;; 9 | PHPUnit) 10 | cd $TRAVIS_BUILD_DIR/web 11 | ../vendor/bin/phpunit 12 | exit $? 13 | ;; 14 | Behat) 15 | cd $TRAVIS_BUILD_DIR/tests 16 | ./behat 17 | exit $? 18 | ;; 19 | *) 20 | echo "Unknown test '$1'" 21 | exit 1 22 | esac 23 | -------------------------------------------------------------------------------- /src/Phing/PhpCodeSnifferConfigurationTask.php: -------------------------------------------------------------------------------- 1 | checkRequirements(); 86 | 87 | $document = new \DOMDocument('1.0', 'UTF-8'); 88 | $document->formatOutput = TRUE; 89 | 90 | // Create the root 'ruleset' element. 91 | $root_element = $document->createElement('ruleset'); 92 | $root_element->setAttribute('name', 'pbm_default'); 93 | $document->appendChild($root_element); 94 | 95 | // Add the description. 96 | $element = $document->createElement('description', 'Default PHP CodeSniffer configuration for composer based Drupal projects.'); 97 | $root_element->appendChild($element); 98 | 99 | // Add the coding standard. 100 | $element = $document->createElement('rule'); 101 | $element->setAttribute('ref', $this->standard); 102 | $root_element->appendChild($element); 103 | 104 | // Add the files to check. 105 | foreach ($this->files as $file) { 106 | $element = $document->createElement('file', $file); 107 | $root_element->appendChild($element); 108 | } 109 | 110 | // Add file extensions. 111 | if (!empty($this->extensions)) { 112 | $extensions = implode(',', $this->extensions); 113 | $this->appendArgument($document, $root_element, $extensions, 'extensions'); 114 | } 115 | 116 | // Add ignore patterns. 117 | foreach ($this->ignorePatterns as $pattern) { 118 | $element = $document->createElement('exclude-pattern', $pattern); 119 | $root_element->appendChild($element); 120 | } 121 | 122 | // Add the report type. 123 | if (!empty($this->report)) { 124 | $this->appendArgument($document, $root_element, $this->report, 'report'); 125 | } 126 | 127 | // Add the shorthand options. 128 | $shorthand_options = array( 129 | 'p' => 'showProgress', 130 | 's' => 'showSniffCodes', 131 | ); 132 | 133 | $options = array_filter($shorthand_options, function ($value) { 134 | return $this->$value; 135 | }); 136 | 137 | if (!empty($options)) { 138 | $this->appendArgument($document, $root_element, implode('', array_flip($options))); 139 | } 140 | 141 | // Save the file. 142 | file_put_contents($this->configFile, $document->saveXML()); 143 | 144 | // If a global configuration file is passed, update this too. 145 | if (!empty($this->globalConfig)) { 146 | $global_config = << '$this->configFile', 150 | ); 151 | PHP; 152 | file_put_contents($this->globalConfig, $global_config); 153 | } 154 | } 155 | 156 | /** 157 | * Appends an argument element to the XML document. 158 | * 159 | * This will append an XML element in the following format: 160 | * 161 | * 162 | * @param \DOMDocument $document 163 | * The document that will contain the argument to append. 164 | * @param \DOMElement $element 165 | * The parent element of the argument to append. 166 | * @param string $value 167 | * The argument value. 168 | * @param string $name 169 | * Optional argument name. 170 | */ 171 | protected function appendArgument(\DOMDocument $document, \DOMElement $element, $value, $name = '') { 172 | $argument = $document->createElement('arg'); 173 | if (!empty($name)) { 174 | $argument->setAttribute('name', $name); 175 | } 176 | $argument->setAttribute('value', $value); 177 | $element->appendChild($argument); 178 | } 179 | 180 | /** 181 | * Checks if all properties required for generating the config are present. 182 | * 183 | * @throws \BuildException 184 | * Thrown when a required property is not present. 185 | */ 186 | protected function checkRequirements() { 187 | $required_properties = array('configFile', 'files', 'standard'); 188 | foreach ($required_properties as $required_property) { 189 | if (empty($this->$required_property)) { 190 | throw new \BuildException("Missing required property '$required_property'."); 191 | } 192 | } 193 | } 194 | 195 | /** 196 | * Sets the path to the configuration file to generate. 197 | * 198 | * @param string $configFile 199 | * The path to the configuration file to generate. 200 | */ 201 | public function setConfigFile($configFile) { 202 | $this->configFile = $configFile; 203 | } 204 | 205 | /** 206 | * Sets the file extensions to scan. 207 | * 208 | * @param string $extensions 209 | * A string containing file extensions, delimited by spaces, commas or 210 | * semicolons. 211 | */ 212 | public function setExtensions($extensions) { 213 | $this->extensions = array(); 214 | $token = ' ,;'; 215 | $extension = strtok($extensions, $token); 216 | while ($extension !== false) { 217 | $this->extensions[] = $extension; 218 | $extension = strtok($token); 219 | } 220 | } 221 | 222 | /** 223 | * Sets the list of files and folders to scan. 224 | * 225 | * @param string $files 226 | * A list of paths, delimited by spaces, commas or semicolons. 227 | */ 228 | public function setFiles($files) { 229 | $this->files = array(); 230 | $token = ' ,;'; 231 | $file = strtok($files, $token); 232 | while ($file !== false) { 233 | $this->files[] = $file; 234 | $file = strtok($token); 235 | } 236 | } 237 | 238 | /** 239 | * Sets the path to the global configuration file to generate. 240 | * 241 | * @param string $globalConfig 242 | * The path to the global configuration file to generate. 243 | */ 244 | public function setGlobalConfig($globalConfig) { 245 | $this->globalConfig = $globalConfig; 246 | } 247 | 248 | /** 249 | * Sets the list of patterns to ignore. 250 | * 251 | * @param string $ignorePatterns 252 | * The list of patterns, delimited by spaces, commas or semicolons. 253 | */ 254 | public function setIgnorePatterns($ignorePatterns) { 255 | $this->ignorePatterns = array(); 256 | $token = ' ,;'; 257 | $pattern = strtok($ignorePatterns, $token); 258 | while ($pattern !== false) { 259 | $this->ignorePatterns[] = $pattern; 260 | $pattern = strtok($token); 261 | } 262 | } 263 | 264 | /** 265 | * Sets the report format to use. 266 | * 267 | * @param string $report 268 | * The report format to use. 269 | */ 270 | public function setReport($report) { 271 | $this->report = $report; 272 | } 273 | 274 | /** 275 | * Sets whether or not to show progress. 276 | * 277 | * @param bool $showProgress 278 | * Whether or not to show progress. 279 | */ 280 | public function setShowProgress($showProgress) { 281 | $this->showProgress = (bool) $showProgress; 282 | } 283 | 284 | /** 285 | * Sets whether or not to show sniff codes in the report. 286 | * 287 | * @param bool $showSniffCodes 288 | * Whether or not to show sniff codes. 289 | */ 290 | public function setShowSniffCodes($showSniffCodes) { 291 | $this->showSniffCodes = (bool) $showSniffCodes; 292 | } 293 | 294 | /** 295 | * Sets the coding standard to use. 296 | * 297 | * @param string $standard 298 | * The coding standard to use. 299 | */ 300 | public function setStandard($standard) { 301 | $this->standard = $standard; 302 | } 303 | 304 | } 305 | -------------------------------------------------------------------------------- /src/Phing/PhpUnitConfigurationTask.php: -------------------------------------------------------------------------------- 1 | checkRequirements(); 86 | 87 | // Load the template file. 88 | $document = new \DOMDocument('1.0', 'UTF-8'); 89 | $document->preserveWhiteSpace = FALSE; 90 | $document->formatOutput = TRUE; 91 | $document->load($this->distFile); 92 | 93 | // Set the base URL. 94 | $this->setEnvironmentVariable('SIMPLETEST_BASE_URL', $this->baseUrl, $document); 95 | 96 | // Set the database URL. 97 | $this->setEnvironmentVariable('SIMPLETEST_DB', $this->dbUrl, $document); 98 | 99 | // Set the path to the browsertest output directory. 100 | $this->setEnvironmentVariable('BROWSERTEST_OUTPUT_DIRECTORY', $this->browsertestOutputDirectory, $document); 101 | 102 | // Set the path to the browsertest output file. 103 | $this->setEnvironmentVariable('BROWSERTEST_OUTPUT_FILE', $this->browsertestOutputFile, $document); 104 | 105 | // Add a test suite for the Drupal project. 106 | $test_suite = $document->createElement('testsuite'); 107 | $test_suite->setAttribute('name', $this->testsuiteName); 108 | 109 | // Append the list of test files. 110 | foreach ($this->files as $file) { 111 | $element = $document->createElement('file', $file); 112 | $test_suite->appendChild($element); 113 | } 114 | 115 | // Append the list of test directories. 116 | foreach ($this->directories as $directory) { 117 | $element = $document->createElement('directory', $directory); 118 | $test_suite->appendChild($element); 119 | } 120 | 121 | // Insert the test suite in the list of test suites. 122 | $test_suites = $document->getElementsByTagName('testsuites')->item(0); 123 | $test_suites->appendChild($test_suite); 124 | 125 | // Save the file. 126 | file_put_contents($this->configFile, $document->saveXML()); 127 | } 128 | 129 | /** 130 | * Sets the value of a pre-existing environment variable. 131 | * 132 | * @param string $variableName 133 | * The name of the environment variable for which to set the value. 134 | * @param string $value 135 | * The value to set. 136 | * @param \DOMDocument $document 137 | * The document in which the change should take place. 138 | */ 139 | protected function setEnvironmentVariable($variableName, $value, \DOMDocument $document) { 140 | /** @var \DOMElement $element */ 141 | foreach ($document->getElementsByTagName('env') as $element) { 142 | if ($element->getAttribute('name') === $variableName) { 143 | $element->setAttribute('value', $value); 144 | break; 145 | } 146 | } 147 | } 148 | /** 149 | * Checks if all properties required for generating the config are present. 150 | * 151 | * @throws \BuildException 152 | * Thrown when a required property is not present. 153 | */ 154 | protected function checkRequirements() { 155 | $required_properties = ['configFile', 'distFile']; 156 | foreach ($required_properties as $required_property) { 157 | if (empty($this->$required_property)) { 158 | throw new \BuildException("Missing required property '$required_property'."); 159 | } 160 | } 161 | } 162 | 163 | /** 164 | * Sets the path to the template of the configuration file. 165 | * 166 | * @param string $distFile 167 | * The path to the template of the configuration file. 168 | */ 169 | public function setDistFile($distFile) { 170 | $this->distFile = $distFile; 171 | } 172 | 173 | /** 174 | * Sets the path to the configuration file to generate. 175 | * 176 | * @param string $configFile 177 | * The path to the configuration file to generate. 178 | */ 179 | public function setConfigFile($configFile) { 180 | $this->configFile = $configFile; 181 | } 182 | 183 | /** 184 | * Sets the list of directories containing test files to execute. 185 | * 186 | * @param string $directories 187 | * A list of directory paths, delimited by spaces, commas or semicolons. 188 | */ 189 | public function setDirectories($directories) { 190 | $this->directories = []; 191 | $token = ' ,;'; 192 | $directory = strtok($directories, $token); 193 | while ($directory !== FALSE) { 194 | $this->directories[] = $directory; 195 | $directory = strtok($token); 196 | } 197 | } 198 | 199 | /** 200 | * Sets the list of test files to execute. 201 | * 202 | * @param string $files 203 | * A list of file paths, delimited by spaces, commas or semicolons. 204 | */ 205 | public function setFiles($files) { 206 | $this->files = []; 207 | $token = ' ,;'; 208 | $file = strtok($files, $token); 209 | while ($file !== FALSE) { 210 | $this->files[] = $file; 211 | $file = strtok($token); 212 | } 213 | } 214 | 215 | /** 216 | * Sets the name of the test suite. 217 | * 218 | * @param string $testsuiteName 219 | * The name of the test suite. 220 | */ 221 | public function setTestsuiteName($testsuiteName) { 222 | $this->testsuiteName = $testsuiteName; 223 | } 224 | 225 | /** 226 | * Sets the base URL. 227 | * 228 | * @param string $baseUrl 229 | * The base URL. 230 | */ 231 | public function setBaseUrl($baseUrl) { 232 | $this->baseUrl = $baseUrl; 233 | } 234 | 235 | /** 236 | * Sets the database URL. 237 | * 238 | * @param string $dbUrl 239 | * The database URL. 240 | */ 241 | public function setDbUrl($dbUrl) { 242 | $this->dbUrl = $dbUrl; 243 | } 244 | 245 | /** 246 | * Sets the path to the browsertest output directory. 247 | * 248 | * @param string $browsertestOutputDirectory 249 | * The path to the directory. 250 | */ 251 | public function setBrowsertestOutputDirectory($browsertestOutputDirectory) { 252 | $this->browsertestOutputDirectory = $browsertestOutputDirectory; 253 | } 254 | 255 | /** 256 | * Sets the path to the browsertest output file. 257 | * 258 | * @param string $browsertestOutputFile 259 | * The path to the file. 260 | */ 261 | public function setBrowsertestOutputFile($browsertestOutputFile) { 262 | $this->browsertestOutputFile = $browsertestOutputFile; 263 | } 264 | 265 | } 266 | -------------------------------------------------------------------------------- /tests/behat: -------------------------------------------------------------------------------- 1 | ../vendor/bin/behat -------------------------------------------------------------------------------- /tests/behat.yml.dist: -------------------------------------------------------------------------------- 1 | default: 2 | suites: 3 | default: 4 | contexts: 5 | - FeatureContext 6 | - Drupal\DrupalExtension\Context\DrupalContext 7 | - Drupal\DrupalExtension\Context\DrushContext 8 | - Drupal\DrupalExtension\Context\MessageContext 9 | - Drupal\DrupalExtension\Context\MinkContext 10 | filters: 11 | tags: '~@wip' 12 | extensions: 13 | Behat\MinkExtension: 14 | base-url: '${behat.base_url}' 15 | files_path: '${behat.files.path}' 16 | goutte: ~ 17 | javascript_session: 'selenium2' 18 | browser_name: '${behat.browser_name}' 19 | selenium2: 20 | wd_host: '${behat.webdriver_url}' 21 | Drupal\DrupalExtension: 22 | api_driver: 'drupal' 23 | blackbox: ~ 24 | drupal: 25 | drupal_root: '${website.drupal.dir}' 26 | selectors: 27 | message_selector: '.messages' 28 | error_message_selector: '.messages.messages--error' 29 | success_message_selector: '.messages.messages--status' 30 | subcontexts: 31 | paths: 32 | - '${behat.subcontexts.path}' 33 | formatters: 34 | ${behat.formatter.name}: ~ 35 | -------------------------------------------------------------------------------- /tests/features/authentication.feature: -------------------------------------------------------------------------------- 1 | Feature: User authentication 2 | In order to protect the integrity of the website 3 | As a product owner 4 | I want to make sure only authenticated users can access the site administration 5 | 6 | Scenario: Anonymous user can see the user login page 7 | Given I am not logged in 8 | When I visit "user" 9 | Then I should see the text "Log in" 10 | And I should see the text "Reset your password" 11 | And I should see the text "Username" 12 | And I should see the text "Password" 13 | But I should not see the text "Log out" 14 | And I should not see the text "My account" 15 | 16 | Scenario Outline: Anonymous user cannot access site administration 17 | Given I am not logged in 18 | When I go to "" 19 | Then I should get an access denied error 20 | 21 | Examples: 22 | | path | 23 | | admin | 24 | | admin/config | 25 | | admin/content | 26 | | admin/people | 27 | | admin/structure | 28 | | node/add | 29 | | node/add/page | 30 | -------------------------------------------------------------------------------- /tests/features/bootstrap/FeatureContext.php: -------------------------------------------------------------------------------- 1 | assertSession()->statusCodeEquals(403); 23 | } 24 | 25 | /** 26 | * Checks that a given image is present in the page. 27 | * 28 | * @Then I (should )see the image :filename 29 | */ 30 | public function assertImagePresent($filename) { 31 | // Drupal appends an underscore and a number to the filename when duplicate 32 | // files are uploaded, for example when a test is run more than once. 33 | // We split up the filename and extension and match for both. 34 | $parts = pathinfo($filename); 35 | $extension = $parts['extension']; 36 | $filename = $parts['filename']; 37 | $this->assertSession()->elementExists('css', "img[src$='.$extension'][src*='$filename']"); 38 | } 39 | 40 | /** 41 | * Checks that a given image is not present in the page. 42 | * 43 | * @Then I should not see the image :filename 44 | */ 45 | public function assertImageNotPresent($filename) { 46 | // Drupal appends an underscore and a number to the filename when duplicate 47 | // files are uploaded, for example when a test is run more than once. 48 | // We split up the filename and extension and match for both. 49 | $parts = pathinfo($filename); 50 | $extension = $parts['extension']; 51 | $filename = $parts['filename']; 52 | $this->assertSession()->elementNotExists('css', "img[src$='.$extension'][src*='$filename']"); 53 | } 54 | 55 | } 56 | --------------------------------------------------------------------------------