diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 0e49b90..0000000 --- a/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2017 Oliver Davies - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/configure-all-the-things/2018-07-24-drupal-bristol/images/1.png b/configure-all-the-things/2018-07-24-drupal-bristol/images/1.png new file mode 100644 index 0000000..a8a2ebf Binary files /dev/null and b/configure-all-the-things/2018-07-24-drupal-bristol/images/1.png differ diff --git a/configure-all-the-things/2018-07-24-drupal-bristol/slides.md b/configure-all-the-things/2018-07-24-drupal-bristol/slides.md new file mode 100644 index 0000000..384b3dc --- /dev/null +++ b/configure-all-the-things/2018-07-24-drupal-bristol/slides.md @@ -0,0 +1,55 @@ +theme: poster, 8 +autoscale: true +build-lists: true +header-emphasis: #53B0EB +header: alignment(center) +text: alignment(left) +text-emphasis: #53B0EB +code: Monaco, line-height(1.2) + +[.hide-footer] +[.header: alignment(center)] + +# Configure all the things!! + +--- + +[.header: alignment(center)] + +# Drupal 8 +### _Configuration management_ + +--- + +[.background-color: #FFFFFF] +[.build-lists: false] +[.header: #111111] +[.text: #111111, alignment(left)] + +![right 800%](../../images/me-phpnw.png) + +- Web Developer & System Administrator +- Senior Developer at Microserve +- Part-time freelancer +- Acquia certified Drupal 8 Grand Master +- Drupal 7 & 8 core contributor +- @opdavies +- www.oliverdavies.uk + +^ Work at Microserve. +Maintain Drupal modules, PHP CLI tools and libraries +Blog on my website + +--- + +## What is configuration? + +--- + + + +[.header: alignment(center)] + +# Thanks! +### _@opdavies_ +### _oliverdavies.uk_ diff --git a/deploying-drupal-ansible-ansistrano/images/ansible.png b/deploying-drupal-ansible-ansistrano/images/ansible.png new file mode 100644 index 0000000..c72d308 Binary files /dev/null and b/deploying-drupal-ansible-ansistrano/images/ansible.png differ diff --git a/deploying-drupal-ansible-ansistrano/images/ansistrano-flow.png b/deploying-drupal-ansible-ansistrano/images/ansistrano-flow.png new file mode 100644 index 0000000..519adaf Binary files /dev/null and b/deploying-drupal-ansible-ansistrano/images/ansistrano-flow.png differ diff --git a/deploying-drupal-ansible-ansistrano/images/ansistrano.png b/deploying-drupal-ansible-ansistrano/images/ansistrano.png new file mode 100644 index 0000000..2954d3d Binary files /dev/null and b/deploying-drupal-ansible-ansistrano/images/ansistrano.png differ diff --git a/deploying-drupal-ansible-ansistrano/images/composer.png b/deploying-drupal-ansible-ansistrano/images/composer.png new file mode 100644 index 0000000..bb95a49 Binary files /dev/null and b/deploying-drupal-ansible-ansistrano/images/composer.png differ diff --git a/deploying-drupal-ansible-ansistrano/images/druplicon.png b/deploying-drupal-ansible-ansistrano/images/druplicon.png new file mode 100644 index 0000000..b2cc145 Binary files /dev/null and b/deploying-drupal-ansible-ansistrano/images/druplicon.png differ diff --git a/deploying-drupal-ansible-ansistrano/images/vagrant.png b/deploying-drupal-ansible-ansistrano/images/vagrant.png new file mode 100644 index 0000000..8636216 Binary files /dev/null and b/deploying-drupal-ansible-ansistrano/images/vagrant.png differ diff --git a/deploying-drupal-ansible-ansistrano/slides.md b/deploying-drupal-ansible-ansistrano/slides.md new file mode 100644 index 0000000..c048ca5 --- /dev/null +++ b/deploying-drupal-ansible-ansistrano/slides.md @@ -0,0 +1,540 @@ +theme: poster, 8 +autoscale: true +build-lists: true +header-emphasis: #53B0EB +header: alignment(left) +text: alignment(left) +text-emphasis: #53B0EB +code: Monaco, line-height(1.2) + +[.header: alignment(center)] + + +# [fit] Deploying PHP applications +# [fit] _With Ansible, Ansible Vault and Ansistrano_ + +--- + +[.background-color: #FFFFFF] +[.build-lists: false] +[.header: #111111] +[.text: #111111, alignment(left)] + +![right 800%](../images/me-phpnw.png) + +- Full Stack Web Developer & System Administrator +- Senior Developer at Microserve +- Part-time freelancer +- Acquia certified Drupal 8 Grand Master +- Drupal 7 & 8 core contributor +- Symfony, Laravel, Sculpin +- @opdavies +- www.oliverdavies.uk + +^ Work at Microserve. +Maintain Drupal modules, PHP CLI tools and libraries +Blog on my website + +--- + +[.background-color: #FFFFFF] +[.build-lists: false] +[.text: #111111, alignment(left)] + +![right 100%](../images/microserve-light.png) + +- https://microserve.io +- https://www.drupal.org/microserve +- https://github.com/microserve-io +- https://twitter.com/microserveltd +- https://www.linkedin.com/company/microserve-ltd + +--- + +[.header: #FFFFFF, line-height(0.7), League Gothic] + +### _Things we'll be_ +# Looking at + +--- + +[.background-color: #FFFFFF] + + +![10%](images/ansible.png) +![85%](images/vagrant.png) +![325%](images/druplicon.png) +![325%](images/composer.png) + +^ Ansible - software provisioning and deployment tool +Vagrant - used for managing virtual machines. Used instead of a real server +Drupal +Composer - required by Drupal 8 to pull in dependencies (e.g. Symfony) + +--- + +1. Ansible crash course +1. Initial setup and provisioning +1. Basic deployment setup +1. Using Ansible Vault for variables +1. Adding and configuring Ansistrano + +--- + +[.background-color: #FFFFFF] +[.text: #111111, alignment(left)] + +## When should I use this? + +* Dedicated hosting: probably has this already +* Shared hosting: probably not flexible enough +* VPS or dedicated server + +--- + +### _What is_ +# Ansible? + +--- + +[.background-color: #FFFFFF] +[.text: #111111] +[.footer: https://en.wikipedia.org/wiki/Ansible_(software)] + +Ansible is _open source software_
that automates _software provisioning_,
_configuration management_,
and _application deployment_. + +--- + +[.background-color: #FFFFFF] +[.text: #111111, alignment(left)] + +![10% right](images/ansible.png) + +# What is Ansible? + +* YAML +* Batteries included +* Executes remote commands +* Installs software packages +* Performs deployment steps + +--- + +[.background-color: #FFFFFF] +[.text: #111111, alignment(left)] + +![10% right](images/ansible.png) + +# What is Ansible? + +* Hosts +* Commands +* Playbooks +* Tasks +* Roles + +--- + + +[.background-color: #FFFFFF] +[.text: #111111, alignment(left)] + +![10% right](images/ansible.png) + +# Why Ansible? + +* Familiar syntax +* Easily readable +* No server dependencies +* Easy to add to an existing project +* Includes relevant modules (e.g. Composer) + +--- + +### _Ansible_ +# Hosts + +--- + +```ini +# hosts.ini + +[dransible] +192.168.33.10 +``` + +--- + +### _Ansible_ +# Commands + +--- + +``` +ansible all -m ping + +ansible all -m command -a 'git pull --chdir=/var/www/app' +``` + +--- + +### _Ansible_ +# [fit] Tasks and Playbooks + +--- + +```yaml +# playbook.yml + +--- +- hosts: all + + tasks: + - name: Update the code + command: git pull + args: + chdir: /var/www/app +``` + +--- + +``` +ansible-playbook playbook.yml -i hosts.ini +``` + +--- + +### _Ansible_ +# Roles + +^ Collections of tasks, variables and handlers + +--- + +```yaml +# requirements.yml + +--- +- src: geerlingguy.apache +- src: geerlingguy.composer +- src: geerlingguy.mysql +- src: geerlingguy.php +- src: geerlingguy.php-mysql +``` + +--- + +``` +ansible-galaxy -r ansible/requirements.yml install +``` + +--- + +```yaml +# playbook.yml + +--- +- hosts: all + + roles: + - geerlingguy.apache + - geerlingguy.mysql + - geerlingguy.php + - geerlingguy.php-mysql + - geerlingguy.composer +``` + +^ Role order matters! + +--- + +[.header: alignment(center)] + +## Let's take a look
at the code + +--- + +### _Basic deployment_ +# Ansible + +^ Any questions on Ansible so far? + +--- + +```yaml +# ansible/provision.yml + +tasks: + - name: Create a database + mysql_db: + name: mydatabase + state: present + + - name: Add the database user + mysql_user: + name: drupal + password: secret + priv: '*.*:ALL' + state: present +``` + +--- + +```yaml +# ansible/deploy.yml + +tasks: + - name: Creating project directory + file: + path: /var/www/app + state: directory + + - name: Uploading application + synchronize: + src: "{{ playbook_dir }}/../" + dest: /var/www/app + + - name: Installing Composer dependencies + composer: + command: install + working_dir: /var/www/app +``` + +--- + +[.background-color: #FFFFFF] +[.text: #111111, alignment(left)] + +# Disadvantages + +* Single point of failure +* No ability to roll back +* Sensitive data stored in plain text + +--- + +### _Keeping secrets_ +# Ansible Vault + +--- + +```bash +ansible-vault create ansible/vault.yml +``` + +--- + +``` +$ANSIBLE_VAULT;1.1;AES256 +36656233323539616336393838396137343939623233393338666530313730373233663263633065 +3133633335316364306366333539613936376239383133330a356365666232623537333730663638 +37393264616134613163663762666464373733663737383039316163336263323538393533323266 +3432346662613438330a386435393432323761386137333736363436386466643031386662353933 +30393631386463313265653862633866663530626439623063393934653235666530656462643135 +39366431353762383434663536663761323565616564336131666339653038326333306433326264 +31623539643166626234663736656337633036323837333137343533386165366531626462643662 +66626631663930626266653937323634366231326537626131663662396335393361336635373736 +3435 +``` + +--- + +```yaml +# ansible/vars/vault.yml + +--- +vault_app_db_name: mydatabase +vault_app_db_user: drupal +vault_app_db_password: secret +``` + +^ Optional, but easier to see where variables are set + +--- + +```yaml +# ansible/vars/vars.yml + +--- +app_db_name: "{{ vault_app_db_name }}" +app_db_user: "{{ vault_app_db_user }}" +app_db_password: "{{ vault_app_db_password }}" +``` + +--- + +```yaml +# ansible/provision.yml + +tasks: + - name: Create a database + mysql_db: + name: '{{ app_db_name }}' + state: present +``` + +--- + +```bash +ansible-vault edit ansible/vault.yml +``` + +--- + +### _Better deployments_ +# Ansistrano + +--- + +![full inline](images/ansistrano.png) + +^ Just another role, specifically for deployments +Ansible port of Capistrano + +--- + +[.background-color: #FFFFFF] +[.text: #111111, alignment(center)] + +Capistrano is an open-source tool for running scripts on multiple servers; its main use is deploying web applications.


It automates the process of making a new version of an application available on one or more web servers, including supporting tasks such as changing databases. + +--- + +[.background-color: #FFFFFF] +[.text: #111111, alignment(left)] + +# Features + +* Multiple release directories +* Shared paths and files +* Customisable +* Multiple deployment strategies +* Multi-stage environments +* Prune old releases +* Rollbacks + +^ rsync, Git, SVN etc + +--- + +```yaml +# ansible/requirements.yml + +--- +... +- ansistrano.deploy +- ansistrano.rollback +``` + +--- + +```yaml +# ansible/deploy.yml + +--- +- hosts: all + + roles: + - ansistrano.deploy +``` + +--- + +```yaml +# ansible/deploy.yml + +--- + ... + vars: + ansistrano_deploy_to: /var/www + ansistrano_deploy_via: git + ansistrano_git_branch: master + ansistrano_git_repo: 'git@github.com:foo/bar.git' +``` + +--- + +```yaml +# ansible/rollback.yml + +--- +- hosts: all + + roles: + - ansistrano.rollback + + vars: + ansistrano_deploy_to: /var/www +``` + +--- + +### _Customising Ansistrano_ +# Build Hooks + +--- + +[.background-color: #FFFFFF] + +![inline 125%](images/ansistrano-flow.png) + +--- + +```yaml +# ansible/deploy.yml + +vars: + ... + ansistrano_after_symlink_shared_tasks_file: "{{ playbook_dir }}/deploy/after-symlink-shared.yml" + ansistrano_after_symlink_tasks_file: "{{ playbook_dir }}/deploy/after-symlink.yml" + ansistrano_after_update_code_tasks_file: "{{ playbook_dir }}/deploy/after-update-code.yml" + + release_web_path: "{{ ansistrano_release_path.stdout }}/web" + release_drush_path: "{{ ansistrano_release_path.stdout }}/vendor/bin/drush" +``` + +--- + +```yaml +# ansible/deploy/after-symlink-shared.yml + +--- +- name: Run database updates + command: '{{ release_drush_path }} --root {{ release_web_path }} updatedb' +``` + +--- + +```yaml +# ansible/deploy/after-update-code.yml + +--- +- name: Install Composer dependencies + composer: + command: install + working_dir: '{{ ansistrano_release_path.stdout }}' +``` + +--- + +```yaml +# ansible/deploy/after-symlink.yml + +--- +- name: Clear Drupal cache + command: '{{ release_drush_path }} --root {{ release_web_path }} cache-rebuild' +``` + +--- + +[.header: alignment(center)] + +# Questions? + +--- + +[.header: alignment(center)] + +# Thanks +### _@opdavies_ +### _oliverdavies.uk_ diff --git a/deploying-drupal-fabric/abstract.md b/deploying-drupal-fabric/abstract.md deleted file mode 100644 index f8a4f13..0000000 --- a/deploying-drupal-fabric/abstract.md +++ /dev/null @@ -1,13 +0,0 @@ -# Deploying Drupal (and anything else) with Fabric - -You’ve built your website, and now you just need to deploy it. There are various ways that this could be done - from (S)FTP, to SCP and rsync, to running commands like “git pull” and “composer install” directly on the server (not recommended). - -My favourite deployment tool of late is [Fabric][0] - a Python based command line tool for running commands locally as well as on remote servers. It’s language and framework agnostic, and unopinionated so you define the steps and workflow that you need - from a basic few-step deployment to a full Capistrano style zero-downtime deployment. - -This talk will cover some introduction to Fabric and how to write your own fabfiles, to then covering some examples and demos of different use case deployments for your Drupal project. - -**Note:** - -I’ve also given a "Deploying PHP applications" version of this talk with more generic examples. - -[0]: http://www.fabfile.org diff --git a/deploying-php-fabric/abstract.md b/deploying-php-fabric/abstract.md deleted file mode 100644 index 9bb6636..0000000 --- a/deploying-php-fabric/abstract.md +++ /dev/null @@ -1,7 +0,0 @@ -# Deploying PHP applications (and anything else) with Fabric - -You’ve built your website, and now you just need to deploy it. There are various ways that this could be done - from (S)FTP, to SCP and rsync, to running commands like git pull and composer install directly on the server which is not ideal. - -My favourite deployment tool of late is Fabric - a Python based command line tool for running commands locally as well as on remote servers. It’s language and framework agnostic, and flexible so you define the steps and workflow that you need - from a basic few-step deployment to a full Capistrano style zero-downtime deployment. - -This talk will cover some introduction to Fabric and how to write your own fabfiles, to then covering some examples and demos of different use case deployments for your application. diff --git a/drupal-dev-days-recap/images/IMG_5278.jpg b/drupal-dev-days-recap/images/IMG_5278.jpg new file mode 100644 index 0000000..b30c532 Binary files /dev/null and b/drupal-dev-days-recap/images/IMG_5278.jpg differ diff --git a/drupal-dev-days-recap/images/autowiring-tweet.png b/drupal-dev-days-recap/images/autowiring-tweet.png new file mode 100644 index 0000000..a35d2d5 Binary files /dev/null and b/drupal-dev-days-recap/images/autowiring-tweet.png differ diff --git a/drupal-dev-days-recap/images/block-aria-roles-2.png b/drupal-dev-days-recap/images/block-aria-roles-2.png new file mode 100644 index 0000000..6840d1f Binary files /dev/null and b/drupal-dev-days-recap/images/block-aria-roles-2.png differ diff --git a/drupal-dev-days-recap/images/block-aria-roles.png b/drupal-dev-days-recap/images/block-aria-roles.png new file mode 100644 index 0000000..54046b5 Binary files /dev/null and b/drupal-dev-days-recap/images/block-aria-roles.png differ diff --git a/drupal-dev-days-recap/images/dev-days-website.png b/drupal-dev-days-recap/images/dev-days-website.png new file mode 100644 index 0000000..2180213 Binary files /dev/null and b/drupal-dev-days-recap/images/dev-days-website.png differ diff --git a/drupal-dev-days-recap/images/dev-days-youtube.png b/drupal-dev-days-recap/images/dev-days-youtube.png new file mode 100644 index 0000000..e108eb5 Binary files /dev/null and b/drupal-dev-days-recap/images/dev-days-youtube.png differ diff --git a/drupal-dev-days-recap/images/gabor-keynote.png b/drupal-dev-days-recap/images/gabor-keynote.png new file mode 100644 index 0000000..db9890f Binary files /dev/null and b/drupal-dev-days-recap/images/gabor-keynote.png differ diff --git a/drupal-dev-days-recap/images/lisbon-map.png b/drupal-dev-days-recap/images/lisbon-map.png new file mode 100644 index 0000000..ef4582a Binary files /dev/null and b/drupal-dev-days-recap/images/lisbon-map.png differ diff --git a/drupal-dev-days-recap/images/membership-renewal.png b/drupal-dev-days-recap/images/membership-renewal.png new file mode 100644 index 0000000..3659d05 Binary files /dev/null and b/drupal-dev-days-recap/images/membership-renewal.png differ diff --git a/drupal-dev-days-recap/images/membership-tweet.png b/drupal-dev-days-recap/images/membership-tweet.png new file mode 100644 index 0000000..b66d25f Binary files /dev/null and b/drupal-dev-days-recap/images/membership-tweet.png differ diff --git a/drupal-dev-days-recap/images/rachel-keynote-2.png b/drupal-dev-days-recap/images/rachel-keynote-2.png new file mode 100644 index 0000000..451e58d Binary files /dev/null and b/drupal-dev-days-recap/images/rachel-keynote-2.png differ diff --git a/drupal-dev-days-recap/images/rachel-keynote.png b/drupal-dev-days-recap/images/rachel-keynote.png new file mode 100644 index 0000000..b5f4d50 Binary files /dev/null and b/drupal-dev-days-recap/images/rachel-keynote.png differ diff --git a/drupal-dev-days-recap/images/rslootjes-slide.png b/drupal-dev-days-recap/images/rslootjes-slide.png new file mode 100644 index 0000000..2fb03f3 Binary files /dev/null and b/drupal-dev-days-recap/images/rslootjes-slide.png differ diff --git a/drupal-dev-days-recap/images/security-slide.png b/drupal-dev-days-recap/images/security-slide.png new file mode 100644 index 0000000..8e8bb0a Binary files /dev/null and b/drupal-dev-days-recap/images/security-slide.png differ diff --git a/drupal-dev-days-recap/images/talk-feedback.png b/drupal-dev-days-recap/images/talk-feedback.png new file mode 100644 index 0000000..1a94d6b Binary files /dev/null and b/drupal-dev-days-recap/images/talk-feedback.png differ diff --git a/drupal-dev-days-recap/slides.md b/drupal-dev-days-recap/slides.md new file mode 100644 index 0000000..d8106c5 --- /dev/null +++ b/drupal-dev-days-recap/slides.md @@ -0,0 +1,101 @@ +theme: plain jane + +# **Drupal Developer Days
2018 Recap** + +![](https://pbs.twimg.com/media/DhbyPqkXkAETonW.jpg) + +--- + +![fit](images/lisbon-map.png) + +--- + +![fit](images/IMG_5278.jpg) + +--- + +![fit](https://pbs.twimg.com/media/DhQiOZAWAAAYPCB.jpg) + +^ 400 people + +--- + +![fit](https://c1.staticflickr.com/3/2934/14139200038_e353a5ea44_k.jpg) + +^ sprints + +--- + +![inline](images/block-aria-roles.png) + +--- + +![inline](images/block-aria-roles-2.png) + +^ Yay, commit credits! + +--- + +![inline](images/gabor-keynote.png) + +^ Composer, JS in core, admin UI, API first +Signs on tables, online meetings and Slack channels +Find the right people +Get involved with something that interests you rather than searching for random issues + +--- + +![inline](images/rslootjes-slide.png) + +^ Symfony Framework style API building in Drupal +symfony guy +things i've done in symfony that I didn't know you could do in D8 +great to see cross-community knowledge sharing + +--- + +![inline](images/autowiring-tweet.png) + +--- + +![inline](images/security-slide.png) + +^ Jess (xjm) - D8 release manager +Responsible disclosure, cross-project collaboration, and Drupal 8 security +Don't include dev dependencies in prod code! +ODL was doing this! +immediate ROI + +--- + +![inline](images/talk-feedback.png) + +--- + +![inline](images/rachel-keynote.png) + +--- + +![inline](images/rachel-keynote-2.png) + +--- + +![inline](https://www.drupal.org/files/drupal-association-logo-rgb.png) + +^ Great to chat about the DA with Rachel + +--- + +![inline](images/membership-tweet.png) + +^ Membership drive + +--- + +![inline](images/membership-renewal.png) + +--- + +![inline](images/dev-days-youtube.png) + +^ Videos are now online diff --git a/drupal-rejoining-php-herd/abstract.md b/drupal-rejoining-php-herd/abstract.md deleted file mode 100644 index 843a668..0000000 --- a/drupal-rejoining-php-herd/abstract.md +++ /dev/null @@ -1,5 +0,0 @@ -# Drupal: Re-joining the PHP Herd - -Drupal 8 was (finally) released on November 19th 2015, after almost 4 years of work and code commits by over 3,200 different contributors. Whilst it’s pretty much the same as the Drupal that we know and, hopefully, love, a lot has changed behind the scenes and under the hood! - -In this talk, I'll highlight some of the new features and improvements in Drupal 8, and discuss some of the benefits to Drupal site builders, themers, and module developers. I'll also talk about some of the non-technical changes and the cultural shift from "not invented here" to "proudly found elsewhere", and how we are rejoining the PHP herd. diff --git a/drupal-testing-workshop/2018-06-28-drupal-bristol/slides.md b/drupal-testing-workshop/2018-06-28-drupal-bristol/slides.md new file mode 100644 index 0000000..ffb2ec8 --- /dev/null +++ b/drupal-testing-workshop/2018-06-28-drupal-bristol/slides.md @@ -0,0 +1,1844 @@ +autoscale: true +build-lists: true +header-emphasis: #53B0EB +header: alignment(left) +text: alignment(left) +text-emphasis: #53B0EB +theme: poster, 8 + +[.header: alignment(center)] + +![](images/title.png) + +# [fit] Drupal Testing Workshop +### _Drupal Bristol, June 2018_ + +--- + +[.build-lists: false] + +- Module and theme developers +- Want to know more about automated testing +- Looking to start writing your first tests + +--- + +[.background-color: #FFFFFF] +[.build-lists: false] +[.header: #111111] +[.text: #111111, alignment(left)] + +![right 800%](../me-phpnw.png) + +- Full stack Web Developer & System Administrator +- Senior Developer at Microserve +- Part-time freelancer +- Acquia certified Drupal 8 Grand Master +- Drupal 7 & 8 core contributor +- opdavies (Drupal.org, GitHub, Twitter) +- www.oliverdavies.uk + +^ Work at Microserve. +Maintain Drupal modules, PHP CLI tools and libraries +Blog on my website + +--- + +[.header: alignment(center)] + +## Test_Driven_Drupal_.com_ + +--- + +[.header: alignment(center)] + +## _Why_ write tests? + +--- + +## _Why write tests?_ + +- Catch bugs earlier +- Piece of mind +- Prevent regressions +- Write less code +- Documentation +- Drupal core requirement - __ +- More important with regular D8 releases + +^ Dave Liddament talk - better and cheaper to catch bugs earlier (e.g. whilst developing rather than after it's been released) +Refer to tests when writing implementation code +ONO merge conflict + +--- + +[.header: alignment(center)] + +## [fit] _Having tests does not mean_ +## [fit] there will be no bugs + +--- + +[.header: alignment(center)] + +## [fit] _Testing may add time now_ +## [fit] but save more time in the future + +--- + +## _Testing in Drupal_ + +- _Drupal 7_ - Simpletest (testing) module provided as part of core +- _Drupal 8_ - PHPUnit added as a core dependency +- _PHPUnit Initiative_ - Simpletest to be deprecated and removed in Drupal 9 + +^ Focussing on PHPUnit today + +--- + +### _Exercise 1_ +## Local site setup + +--- + +[.header: #53B0EB] + +## Docksal + +- Docker based local development environment +- Microserve standard +- Open source +- Per site configuration and customisation +- fin CLI, Apache, MySQL, Solr, Varnish, Mailhog, PHPMyAdmin etc +- Virtualbox or native Docker +- Can slow down tests +- Provides consistency + +--- + +- https://github.com/opdavies/drupal-testing-workshop +- https://docksal.io/installation +- git clone +- fin init +- http://drupaltest.docksal + +^ Contains Drupal 8 with Composer, examples module + +--- + +### _Exercise 2_ +## Running Tests + +--- + +### _Option 1_ +## Simpletest module (UI) + +--- + +![fit](images/d8-simpletest-1.png) + +--- + +![fit](images/d8-simpletest-2.png) + +--- + +![fit](images/d8-simpletest-3.png) + +--- + +![fit](images/d8-simpletest-4.png) + +--- + +![fit](images/d8-simpletest-5.png) + +--- + +![fit](images/d8-simpletest-6.png) + +--- + +![fit](images/d8-simpletest-7.png) + +--- + +### _Option 2_ +## Command line + +--- + +## Prerequisite _(creating a phpunit.xml file)_ + +- Configures PHPUnit +- Needed to run some types of tests +- Ignored by Git by default +- Copy _core/phpunit.xml.dist_ to _core/phpunit.xml_ +- Add and change as needed + - `SIMPLETEST_BASE_URL`, `SIMPLETEST_DB`, `BROWSERTEST_OUTPUT_DIRECTORY` + - `stopOnFailure="true"` + +--- + +``` +cd web + +../vendor/bin/phpunit -c core \ +modules/contrib/examples/phpunit_example +``` + +--- + +``` +cd web/core + +../../vendor/bin/phpunit \ +../modules/contrib/examples/phpunit_example +``` + +--- + +## Pro-tip: Add paths to _$PATH_ + +```bash +# ~/.zshrc + +export PATH=$HOME/bin:/usr/local/bin:$PATH + +export PATH=vendor/bin:$PATH +export PATH=../vendor/bin:$PATH +export PATH=node_modules/.bin:$PATH +``` + +--- + +### _Option 2_ +## CLI with Docksal + +--- + +``` +fin bash + +cd web + +../vendor/bin/phpunit -c core \ +modules/contrib/examples/phpunit_example +``` + +--- + +``` +fin bash + +cd web/core + +../../vendor/bin/phpunit \ +../modules/contrib/examples/phpunit_example +``` + +--- + +### _Option 3_ +## Docksal PHPUnit addon + +--- + +- Custom Docksal command +- Submitted to the Docksal addons repo +- _fin addon install phpunit_ +- Wrapper around phpunit command +- Copies a stub phpunit.xml file if exists, or duplicates phpunit.xml.dist +- Shorter command, combines two actions + +^ Checks for core/phpunit.xml on each test run +Will create one if is not present + +--- + +``` +fin phpunit web/modules/contrib/examples/phpunit_example +``` + +--- + +``` +fin phpunit web/modules/contrib/examples/phpunit_example + + +Copying /var/www/.docksal/drupal/core/phpunit.xml to /var/www/web/core/phpunit.xml +PHPUnit 6.5.8 by Sebastian Bergmann and contributors. + +Testing web/modules/contrib/examples/phpunit_example +.................................. 34 / 34 (100%) + +Time: 46.8 seconds, Memory: 6.00MB + +OK (34 tests, 41 assertions) +``` + +--- + +``` +fin phpunit web/modules/contrib/examples/phpunit_example + + +Copying /var/www/web/core/phpunit.xml.dist to /var/www/web/core/phpunit.xml. +Please edit it's values as needed and re-run 'fin phpunit'. +``` +--- + +``` +fin phpunit web/modules/contrib/examples/phpunit_example + + +PHPUnit 6.5.8 by Sebastian Bergmann and contributors. + +Testing web/modules/contrib/examples/phpunit_example +.................................. 34 / 34 (100%) + +Time: 48.62 seconds, Memory: 6.00MB + +OK (34 tests, 41 assertions) +``` + +--- + +### _Option 4_ +## IDE/text editor integration + +--- + +![fit](images/phpstorm-integration.png) + +--- + +[.header: alignment(center)] + +## Types of tests + +--- + +[.header: alignment(center)] + +## _1._ Arrange +## _2._ Act +## _3._ Assert + +--- + +[.header: #53B0EB] + +## Functional tests + +- Tests functionality +- Interacts with database +- Full Drupal installation +- Slower to run +- With/without JavaScript + +^ testing profile + +--- + +### _Exercise_ +## Let's write a
functional test + +--- + +- Create a _web/modules/custom/drupalbristol_ directory +- Create a `drupalbristol.info.yml` file + +--- + +``` +# drupalbristol.info.yml + +name: Drupal Bristol +core: 8.x +type: module +``` + +--- + +- Create a _tests/src/Functional_ directory +- Create an _ExampleFunctionalTest.php_ file + +--- + + +```php +// ExampleFunctionalTest.php + +namespace Drupal\Tests\drupalbristol\Functional; + +use Drupal\Tests\BrowserTestBase; + +class ExampleFunctionalTest extends BrowserTestBase { + +} + +``` + +--- + +```php + +// ExampleFunctionalTest.php + +public function testExamplePage() { + $this->drupalGet('/example-one'); + + $this->assertSession()->statusCodeEquals(200); +} +``` + +--- + +``` +PHPUnit 6.5.8 by Sebastian Bergmann and contributors. + +Testing Drupal\Tests\drupalbristol\Functional\ExampleFunctionalTest + +Behat\Mink\Exception\ExpectationException : Current response status code is 404, but 200 expected. + /var/www/vendor/behat/mink/src/WebAssert.php:768 + /var/www/vendor/behat/mink/src/WebAssert.php:130 + /var/www/web/modules/custom/drupalbristol/tests/src/Functional/ExampleFunctionalTest.php:14 + +Time: 18.2 seconds, Memory: 6.00MB + +ERRORS! +Tests: 1, Assertions: 2, Errors: 1. +``` + +--- + +- Create a _drupalbristol.routing.yml_ file +- Create a Controller + +--- + +```yaml +# drupalbristol.routing.yml + +drupalbristol.example: + path: '/example-one' + defaults: + _controller: 'Drupal\drupalbristol\Controllers\ExampleController::index' + requirements: + _access: 'TRUE' +``` + +--- + +```php +// src/Controllers/ExampleController.php + +namespace Drupal\drupalbristol\Controllers; + +use Drupal\Core\Controller\ControllerBase; + +class ExampleController extends ControllerBase { + + public function index() { + return ['#markup' => $this->t('Drupal Testing Workshop')]; + } + +} +``` + +--- + +```php +class ExampleFunctionalTest extends BrowserTestBase { + + protected static $modules = ['drupalbristol']; + + ... +} +``` + +--- + +[.header: #53B0EB] + +## Kernel tests + +- Integration tests +- Can install modules, interact with services, container, database +- Minimal Drupal bootstrap +- Faster than functional tests +- More setup required + +--- + +### _Exercise_ +## Let's write a
kernel test + +--- + +- Create a _tests/src/Kernel directory +- Create an _ExampleKernelTest.php_ file +- Create a Service +- Use the service within the test to perform an action + +--- + +```php +// tests/src/Kernel/ExampleKernelTest.php + +namespace Drupal\Tests\drupalbristol\Kernel; + +use Drupal\KernelTests\Core\Entity\EntityKernelTestBase; +use Drupal\user\Entity\User; + +class ExampleKernelTest extends EntityKernelTestBase { + + public static $modules = ['drupalbristol']; + + public function testUserDeleter() { + } + +} + +``` + +--- + +```php +// tests/src/Kernel/ExampleKernelTest.php + +public function testUserDeleter { + $user = $this->createUser(); + + $this->assertInstanceOf(User::class, $user); + + /** @var \Drupal\drupalbristol\Services\UserDeleter $user_deleter */ + $user_deleter = \Drupal::service('drupalbristol.user_deleter'); + $user_deleter->delete($user); + + $user = $this->reloadEntity($user); + + $this->assertNull($user); +} +``` + +--- + +``` +PHPUnit 6.5.8 by Sebastian Bergmann and contributors. + +Testing Drupal\Tests\drupalbristol\Kernel\ExampleKernelTest + +Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException : You have requested a non-existent service "drupalbristol.user_deleter". + /var/www/vendor/symfony/dependency-injection/ContainerBuilder.php:1043 + /var/www/vendor/symfony/dependency-injection/ContainerBuilder.php:610 + /var/www/vendor/symfony/dependency-injection/ContainerBuilder.php:588 + /var/www/web/core/lib/Drupal.php:159 + /var/www/web/modules/custom/drupalbristol/tests/src/Kernel/ExampleKernelTest.php:24 + +Time: 7.06 seconds, Memory: 6.00MB + + +ERRORS! +Tests: 1, Assertions: 3, Errors: 1. + +Process finished with exit code 2 +``` + +--- + +```yaml +# drupalbristol.services.yml + +services: + drupalbristol.user_deleter: + class: 'Drupal\drupalbristol\Services\UserDeleter' + arguments: [] +``` + +--- + +```php +// src/Services/UserDeleter.php + +namespace Drupal\drupalbristol\Services; + +use Drupal\Core\Session\AccountInterface; + +class UserDeleter { + + public function delete(AccountInterface $user) { + user_delete($user->id()); + } + +} +``` + +--- + +``` +PHPUnit 6.5.8 by Sebastian Bergmann and contributors. + +Testing Drupal\Tests\drupalbristol\Kernel\ExampleKernelTest + +Drupal\Core\Entity\EntityStorageException : SQLSTATE[HY000]: General error: 1 no such table: test89378988.users_data: DELETE FROM {users_data} +WHERE uid IN (:db_condition_placeholder_0); Array +( + [:db_condition_placeholder_0] => 1 +) + + /var/www/web/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php:777 + /var/www/web/core/includes/entity.inc:281 + /var/www/web/core/modules/user/user.module:878 + /var/www/web/core/modules/user/user.module:865 + /var/www/web/modules/custom/drupalbristol/src/Services/UserDeleter.php:10 + /var/www/web/modules/custom/drupalbristol/tests/src/Kernel/ExampleKernelTest.php:25 + + Caused by + Drupal\Core\Database\DatabaseExceptionWrapper: SQLSTATE[HY000]: General error: 1 no such table: test89378988.users_data: DELETE FROM {users_data} + WHERE uid IN (:db_condition_placeholder_0); Array + ( + [:db_condition_placeholder_0] => 1 + ) + +Time: 6.55 seconds, Memory: 6.00MB + +ERRORS! +Tests: 1, Assertions: 3, Errors: 1. + +Process finished with exit code 2 +``` + +--- + +```php +// tests/src/Kernel/ExampleKernelTest.php + +protected function setUp() { + parent::setUp(); + + $this->installSchema('user', ['users_data']); +} +``` + +--- + +``` +PHPUnit 6.5.8 by Sebastian Bergmann and contributors. + +Testing Drupal\Tests\drupalbristol\Kernel\ExampleKernelTest + + +Time: 7.38 seconds, Memory: 6.00MB + +OK (1 test, 5 assertions) + +Process finished with exit code 0 + +``` + +--- + +[.header: #53B0EB] + +## Unit tests + +- Tests PHP logic +- No database interaction +- Fast to run +- Tightly coupled +- Mocking dependencies +- Hard to refactor + +--- + +### _Exercise_ +## Let's write a
unit test + +--- + +```php +// tests/src/Unit/Services/ExampleUnitTest.php + +namespace Drupal\Tests\drupalbristol\Unit; + +use Drupal\Tests\UnitTestCase; + +class ExampleUnitTest extends UnitTestCase { + + public function testAdd() { + $this->assertEquals(5, (new Calculator(3))->add(2)->calculate()); + } +} +``` + +--- + +``` +PHPUnit 6.5.8 by Sebastian Bergmann and contributors. + +Testing Drupal\Tests\drupalbristol\Unit\ExampleUnitTest + +Error : Class 'Drupal\Tests\drupalbristol\Unit\Calculator' not found + /var/www/web/modules/custom/drupalbristol/tests/src/Unit/Services/ExampleUnitTest.php:10 + +Time: 5.13 seconds, Memory: 6.00MB + + +ERRORS! +Tests: 1, Assertions: 0, Errors: 1. +``` + +--- + +```php +// src/Services/Calculator.php + +namespace Drupal\drupalbristol\Services; + +class Calculator { + + private $total; + + public function __construct($value) { + $this->total = $value; + } + + public function add($value) { + $this->total += $value; + + return $this; + } + + public function calculate() { + return $this->total; + } + +} +``` + +--- + +```php +// tests/src/Unit/Services/ExampleUnitTest.php + +namespace Drupal\Tests\drupalbristol\Unit; + +use Drupal\drupalbristol\Services\Calculator; +use Drupal\Tests\UnitTestCase; + +class ExampleUnitTest extends UnitTestCase { + + public function testAdd() { + $this->assertEquals(5, (new Calculator(3))->add(2)->calculate()); + } + +} +``` + +--- + +``` +PHPUnit 6.5.8 by Sebastian Bergmann and contributors. + +Testing Drupal\Tests\drupalbristol\Unit\ExampleUnitTest + + +Time: 4.55 seconds, Memory: 4.00MB + +OK (1 test, 1 assertion) +``` + +--- + +[.header: alignment(center)] + +## Test driven
development _(TDD)_ + +--- + + +## _Test Driven Development_ + +- Write a test +- Test fails +- Write code +- Test passes +- Refactor +- Repeat + +--- + +[.background-color: #FFFFFF] +[.footer: https://github.com/foundersandcoders/testing-tdd-intro] +[.footer-style: #2F2F2F] + +![100%](images/tdd-loop.png) + +--- + +## _How I Write Tests - "Outside In"_ + +- Start with functional tests +- Drop down to kernel or unit tests where needed +- Programming by wishful thinking +- Write comments first, then fill in the code +- Sometimes write assertions first + +--- + +### _Exercise_ +## Let's build a blog using test driven development + + +--- + +## _Acceptance criteria_ + +- As a site visitor +- I want to see a list of published articles at /blog +- Ordered by post date + +--- + +## _Tasks_ + +- Ensure the blog page exists +- Ensure only published articles are shown +- Ensure the articles are shown in the correct order + +--- + +## _Implementation_ + +- Use views module +- Do the mininum amount at each step, make no assumptions, let the tests guide us +- Start with functional test + +--- + +### _Step 1_ +## Create the module + +--- + +```yml +# tdd_blog.info.yml + +name: 'TDD Blog' +core: '8.x' +type: 'module' +``` + +--- + +### _Step 2_ +## Ensure the blog page exists + +--- + + +```php +drupalGet('/blog'); + + $this->assertSession()->statusCodeEquals(200); + } + +} +``` + +--- + + +```php, [.highlight: 5] +drupalGet('/blog'); + + $this->assertSession()->statusCodeEquals(200); + } + +} +``` + +--- + + +```php, [.highlight: 7-9] +drupalGet('/blog'); + + $this->assertSession()->statusCodeEquals(200); + } + +} +``` + +--- + + +```php, [.highlight: 11] +drupalGet('/blog'); + + $this->assertSession()->statusCodeEquals(200); + } + +} +``` + +--- + + +```php, [.highlight: 13-17] +drupalGet('/blog'); + + $this->assertSession()->statusCodeEquals(200); + } + +} +``` + +--- + + +```bash, [.highlight: 1] +docker@cli:/var/www/web$ ../vendor/bin/phpunit -c core modules/custom/tdd_blog +PHPUnit 6.5.8 by Sebastian Bergmann and contributors. + +Testing modules/custom/tdd_blog +E 1 / 1 (100%) + +Time: 19.31 seconds, Memory: 6.00MB + +There was 1 error: + +1) Drupal\Tests\tdd_blog\Functional\BlogPageTest::testBlogPageExists +Behat\Mink\Exception\ExpectationException: Current response status code is 404, but 200 expected. + +/var/www/vendor/behat/mink/src/WebAssert.php:768 +/var/www/vendor/behat/mink/src/WebAssert.php:130 +/var/www/web/modules/custom/tdd_blog/tests/src/Functional/BlogPageTest.php:13 + +ERRORS! +Tests: 1, Assertions: 3, Errors: 1. +``` + +--- + + +```bash, [.highlight: 4] +docker@cli:/var/www/web$ ../vendor/bin/phpunit -c core modules/custom/tdd_blog +PHPUnit 6.5.8 by Sebastian Bergmann and contributors. + +Testing modules/custom/tdd_blog +E 1 / 1 (100%) + +Time: 19.31 seconds, Memory: 6.00MB + +There was 1 error: + +1) Drupal\Tests\tdd_blog\Functional\BlogPageTest::testBlogPageExists +Behat\Mink\Exception\ExpectationException: Current response status code is 404, but 200 expected. + +/var/www/vendor/behat/mink/src/WebAssert.php:768 +/var/www/vendor/behat/mink/src/WebAssert.php:130 +/var/www/web/modules/custom/tdd_blog/tests/src/Functional/BlogPageTest.php:13 + +ERRORS! +Tests: 1, Assertions: 3, Errors: 1. +``` + +--- + + +```bash, [.highlight: 5-13] +docker@cli:/var/www/web$ ../vendor/bin/phpunit -c core modules/custom/tdd_blog +PHPUnit 6.5.8 by Sebastian Bergmann and contributors. + +Testing modules/custom/tdd_blog +E 1 / 1 (100%) + +Time: 19.31 seconds, Memory: 6.00MB + +There was 1 error: + +1) Drupal\Tests\tdd_blog\Functional\BlogPageTest::testBlogPageExists +Behat\Mink\Exception\ExpectationException: Current response status code is 404, but 200 expected. + +/var/www/vendor/behat/mink/src/WebAssert.php:768 +/var/www/vendor/behat/mink/src/WebAssert.php:130 +/var/www/web/modules/custom/tdd_blog/tests/src/Functional/BlogPageTest.php:13 + +ERRORS! +Tests: 1, Assertions: 3, Errors: 1. +``` + +--- + + +```bash, [.highlight: 14-16] +docker@cli:/var/www/web$ ../vendor/bin/phpunit -c core modules/custom/tdd_blog +PHPUnit 6.5.8 by Sebastian Bergmann and contributors. + +Testing modules/custom/tdd_blog +E 1 / 1 (100%) + +Time: 19.31 seconds, Memory: 6.00MB + +There was 1 error: + +1) Drupal\Tests\tdd_blog\Functional\BlogPageTest::testBlogPageExists +Behat\Mink\Exception\ExpectationException: Current response status code is 404, but 200 expected. + +/var/www/vendor/behat/mink/src/WebAssert.php:768 +/var/www/vendor/behat/mink/src/WebAssert.php:130 +/var/www/web/modules/custom/tdd_blog/tests/src/Functional/BlogPageTest.php:13 + +ERRORS! +Tests: 1, Assertions: 3, Errors: 1. +``` + +--- + + +```bash, [.highlight: 18-19] +docker@cli:/var/www/web$ ../vendor/bin/phpunit -c core modules/custom/tdd_blog +PHPUnit 6.5.8 by Sebastian Bergmann and contributors. + +Testing modules/custom/tdd_blog +E 1 / 1 (100%) + +Time: 19.31 seconds, Memory: 6.00MB + +There was 1 error: + +1) Drupal\Tests\tdd_blog\Functional\BlogPageTest::testBlogPageExists +Behat\Mink\Exception\ExpectationException: Current response status code is 404, but 200 expected. + +/var/www/vendor/behat/mink/src/WebAssert.php:768 +/var/www/vendor/behat/mink/src/WebAssert.php:130 +/var/www/web/modules/custom/tdd_blog/tests/src/Functional/BlogPageTest.php:13 + +ERRORS! +Tests: 1, Assertions: 3, Errors: 1. +``` + +--- + +- _The view has not been created_ +- Create a new view +- Set the path +- Export the config +- Copy it into the module's `config/install` directory + +--- + + +```[.highlight: 11-13] +docker@cli:/var/www/web$ ../vendor/bin/phpunit -c core modules/custom/tdd_blog +PHPUnit 6.5.8 by Sebastian Bergmann and contributors. + +Testing modules/custom/tdd_blog +E 1 / 1 (100%) + +Time: 16.02 seconds, Memory: 6.00MB + +There was 1 error: + +1) Drupal\Tests\tdd_blog\Functional\BlogPageTest::testBlogPageExists +Drupal\Core\Config\UnmetDependenciesException: Configuration objects provided by tdd_blog +have unmet dependencies: views.view.blog (node.type.article, node, views) + +/var/www/web/core/lib/Drupal/Core/Config/UnmetDependenciesException.php:98 +/var/www/web/core/lib/Drupal/Core/Config/ConfigInstaller.php:469 +/var/www/web/core/lib/Drupal/Core/ProxyClass/Config/ConfigInstaller.php:132 +/var/www/web/core/lib/Drupal/Core/Extension/ModuleInstaller.php:145 +/var/www/web/core/lib/Drupal/Core/ProxyClass/Extension/ModuleInstaller.php:83 +/var/www/web/core/lib/Drupal/Core/Test/FunctionalTestSetupTrait.php:437 +/var/www/web/core/tests/Drupal/Tests/BrowserTestBase.php:1055 +/var/www/web/core/tests/Drupal/Tests/BrowserTestBase.php:490 + +ERRORS! +Tests: 1, Assertions: 0, Errors: 1. +``` + +--- + + +```yml,[.highlight: 1, 7-10] +# tdd_blog.info.yml + +name: 'TDD Dublin' +description: 'A demo module to show test driven module development.' +core: 8.x +type: module + +dependencies: + - 'drupal:node' + - 'drupal:views' +``` + +--- + + +```[.highlight: 10-13] +docker@cli:/var/www/web$ ../vendor/bin/phpunit -c core modules/custom/tdd_blog +PHPUnit 6.5.8 by Sebastian Bergmann and contributors. + +Testing modules/custom/tdd_blog +E 1 / 1 (100%) + +Time: 20 seconds, Memory: 6.00MB + +There was 1 error: + +1) Drupal\Tests\tdd_blog\Functional\BlogPageTest::testBlogPageExists +Drupal\Core\Config\UnmetDependenciesException: Configuration objects provided by tdd_blog +have unmet dependencies: views.view.blog (node.type.article) + +/var/www/web/core/lib/Drupal/Core/Config/UnmetDependenciesException.php:98 +/var/www/web/core/lib/Drupal/Core/Config/ConfigInstaller.php:469 +/var/www/web/core/lib/Drupal/Core/ProxyClass/Config/ConfigInstaller.php:132 +/var/www/web/core/lib/Drupal/Core/Extension/ModuleInstaller.php:145 +/var/www/web/core/lib/Drupal/Core/ProxyClass/Extension/ModuleInstaller.php:83 +/var/www/web/core/lib/Drupal/Core/Test/FunctionalTestSetupTrait.php:437 +/var/www/web/core/tests/Drupal/Tests/BrowserTestBase.php:1055 +/var/www/web/core/tests/Drupal/Tests/BrowserTestBase.php:490 + +ERRORS! +Tests: 1, Assertions: 0, Errors: 1. +``` + +--- + +- Add the article content type + +--- + + +```[.highlight: 5, 9] +docker@cli:/var/www/web$ ../vendor/bin/phpunit -c core modules/custom/tdd_blog +PHPUnit 6.5.8 by Sebastian Bergmann and contributors. + +Testing modules/custom/tdd_blog +. 1 / 1 (100%) + +Time: 23.36 seconds, Memory: 6.00MB + +OK (1 test, 3 assertions) +``` + +--- + +[.build-lists: false] + +## _Tasks_ + +- ~~Ensure the blog page exists~~ +- Ensure only published articles are shown +- Ensure the articles are shown in the correct order + +--- + +### _Step 3_ +## Ensure only published articles are shown + +--- + + +```php +public function testOnlyPublishedArticlesAreShown() { + // Given I have a mixture of published and unpublished articles, + // as well as other types of content. + + // When I view the blog page. + + // I should only see the published articles. +} +``` + +--- + +### _Option 1_ +## Functional tests + +--- + + +```php,[.highlight: 1, 4-8] +// modules/custom/tdd_blog/tests/src/Functional/BlogPageTest.php + +public function testOnlyPublishedArticlesAreShown() { + // Given I have a mixture of published and unpublished articles, + // as well as other types of content. + $node1 = $this->drupalCreateNode(['type' => 'page', 'status' => 1]); + $node2 = $this->drupalCreateNode(['type' => 'article', 'status' => 1]); + $node3 = $this->drupalCreateNode(['type' => 'article', 'status' => 0]); + + // When I view the blog page. + $this->drupalGet('/blog'); + + // I should only see the published articles. + $assert = $this->assertSession(); + $assert->pageTextContains($node2->label()); + $assert->pageTextNotContains($node1->label()); + $assert->pageTextNotContains($node3->label()); +} +``` + +^ Different ways to achieve this. This is taking the functional test approach. + +--- + + +```php,[.highlight: 10-12] +// modules/custom/tdd_blog/tests/src/Functional/BlogPageTest.php + +public function testOnlyPublishedArticlesAreShown() { + // Given I have a mixture of published and unpublished articles, + // as well as other types of content. + $node1 = $this->drupalCreateNode(['type' => 'page', 'status' => 1]); + $node2 = $this->drupalCreateNode(['type' => 'article', 'status' => 1]); + $node3 = $this->drupalCreateNode(['type' => 'article', 'status' => 0]); + + // When I view the blog page. + $this->drupalGet('/blog'); + + // I should only see the published articles. + $assert = $this->assertSession(); + $assert->pageTextContains($node2->label()); + $assert->pageTextNotContains($node1->label()); + $assert->pageTextNotContains($node3->label()); +} +``` + +--- + + +```php, [.highlight: 13-17] +// modules/custom/tdd_blog/tests/src/Functional/BlogPageTest.php + +public function testOnlyPublishedArticlesAreShown() { + // Given I have a mixture of published and unpublished articles, + // as well as other types of content. + $node1 = $this->drupalCreateNode(['type' => 'page', 'status' => 1]); + $node2 = $this->drupalCreateNode(['type' => 'article', 'status' => 1]); + $node3 = $this->drupalCreateNode(['type' => 'article', 'status' => 0]); + + // When I view the blog page. + $this->drupalGet('/blog'); + + // I should only see the published articles. + $assert = $this->assertSession(); + $assert->pageTextContains($node2->label()); + $assert->pageTextNotContains($node1->label()); + $assert->pageTextNotContains($node3->label()); +} +``` + +--- + +### _Option 2_ +## Kernel tests + +--- + + +```php +createNode(['type' => 'page', 'status' => 1]); + $this->createNode(['type' => 'article', 'status' => 1]); + $this->createNode(['type' => 'article', 'status' => 0]); + } + +} +``` + +^ Kernel test approach +Dropping down a level +No need for the brower, not asserting against HTML +Faster to run + +--- + + +```php, [.highlight: 3-8] +createNode(['type' => 'page', 'status' => 1]); + $this->createNode(['type' => 'article', 'status' => 1]); + $this->createNode(['type' => 'article', 'status' => 0]); + } + +} +``` + +--- + + +```php, [.highlight: 10] +createNode(['type' => 'page', 'status' => 1]); + $this->createNode(['type' => 'article', 'status' => 1]); + $this->createNode(['type' => 'article', 'status' => 0]); + } + +} +``` + +--- + + +```php, [.highlight: 14-18] +createNode(['type' => 'page', 'status' => 1]); + $this->createNode(['type' => 'article', 'status' => 1]); + $this->createNode(['type' => 'article', 'status' => 0]); + } + +} +``` + +--- + + +```[.highlight: 9-16] +docker@cli:/var/www/web$ ../vendor/bin/phpunit -c core modules/custom/tdd_blog/tests/src/Kernel/ +PHPUnit 6.5.8 by Sebastian Bergmann and contributors. + +Testing modules/custom/tdd_blog/tests/src/Kernel/ +E 1 / 1 (100%) + +Time: 6.22 seconds, Memory: 6.00MB + +There was 1 error: + +1) Drupal\Tests\tdd_blog\Kernel\BlogPageTest::testOnlyPublishedArticlesAreShown +Error: Call to a member function id() on boolean + +/var/www/web/core/modules/filter/filter.module:212 +/var/www/web/core/modules/node/tests/src/Traits/NodeCreationTrait.php:73 +/var/www/web/modules/custom/tdd_blog/tests/src/Kernel/BlogPageTest.php:13 + +ERRORS! +Tests: 1, Assertions: 2, Errors: 1. +``` + +--- + + +```php, [.highlight: 2] +public function testOnlyPublishedArticlesAreShown() { + $this->installConfig(['filter']); + + $this->createNode(['type' => 'page', 'status' => 1]); + $this->createNode(['type' => 'article', 'status' => 1]); + $this->createNode(['type' => 'article', 'status' => 0]); +} +``` + +--- + + +```php, [.highlight: 8] +public function testOnlyPublishedArticlesAreShown() { + $this->installConfig(['filter']); + + $this->createNode(['type' => 'page', 'status' => 1]); + $this->createNode(['type' => 'article', 'status' => 1]); + $this->createNode(['type' => 'article', 'status' => 0]); + + $results = views_get_view_result('blog'); +} +``` + +--- + + +```php, [.highlight: 3] +... + +public static $modules = ['node', 'tdd_blog', 'views']; + +public function testOnlyPublishedArticlesAreShown() { + $this->installConfig(['filter', 'tdd_blog']); + + $this->createNode(['type' => 'page', 'status' => 1]); + $this->createNode(['type' => 'article', 'status' => 1]); + $this->createNode(['type' => 'article', 'status' => 0]); + + $results = views_get_view_result('blog'); + + $this->assertCount(1, $results); + $this->assertEquals(2, $results[0]->_entity->id()); +} +``` + +--- + + +```php, [.highlight: 6] +... + +public static $modules = ['node', 'tdd_blog', 'views']; + +public function testOnlyPublishedArticlesAreShown() { + $this->installConfig(['filter', 'tdd_blog']); + + $this->createNode(['type' => 'page', 'status' => 1]); + $this->createNode(['type' => 'article', 'status' => 1]); + $this->createNode(['type' => 'article', 'status' => 0]); + + $results = views_get_view_result('blog'); + + $this->assertCount(1, $results); + $this->assertEquals(2, $results[0]->_entity->id()); +} +``` + +--- + + +```php, [.highlight: 8-15] +... + +public static $modules = ['node', 'tdd_blog', 'views']; + +public function testOnlyPublishedArticlesAreShown() { + $this->installConfig(['filter', 'tdd_blog']); + + $this->createNode(['type' => 'page', 'status' => 1]); + $this->createNode(['type' => 'article', 'status' => 1]); + $this->createNode(['type' => 'article', 'status' => 0]); + + $results = views_get_view_result('blog'); + + $this->assertCount(1, $results); + $this->assertEquals(2, $results[0]->_entity->id()); +} +``` + +^ Assert +Should only be one result, should be node 2 +Node IDs are reset on each test method + +--- + + +``` +PHPUnit 6.5.8 by Sebastian Bergmann and contributors. + +Testing web/modules/custom/tdd_blog/tests/src/Kernel +F 1 / 1 (100%) + +Time: 2.16 seconds, Memory: 6.00MB + +There was 1 failure: + +1) Drupal\Tests\tdd_blog\Kernel\BlogPageTest::testOnlyPublishedArticlesAreShown +Failed asserting that actual size 3 matches expected size 1. + +/Users/opdavies/Code/drupal-testing-workshop/web/modules/custom/tdd_blog/tests/src/Kernel/BlogPageTest.php:23 + +FAILURES! +Tests: 1, Assertions: 4, Failures: 1. +``` + +--- + + +```[.highlight: 8-13] +PHPUnit 6.5.8 by Sebastian Bergmann and contributors. + +Testing web/modules/custom/tdd_blog/tests/src/Kernel +F 1 / 1 (100%) + +Time: 2.16 seconds, Memory: 6.00MB + +There was 1 failure: + +1) Drupal\Tests\tdd_blog\Kernel\BlogPageTest::testOnlyPublishedArticlesAreShown +Failed asserting that actual size 3 matches expected size 1. + +/Users/opdavies/Code/drupal-testing-workshop/web/modules/custom/tdd_blog/tests/src/Kernel/BlogPageTest.php:23 + +FAILURES! +Tests: 1, Assertions: 4, Failures: 1. +``` + +--- + +>- _There are no filters on the view_ +- Add the filters +- Export and save the view + +--- + +```[.highlight: 3-8] +PHPUnit 6.5.8 by Sebastian Bergmann and contributors. + +Testing web/modules/custom/tdd_blog/tests/src/Kernel +. 1 / 1 (100%) + +Time: 2.02 seconds, Memory: 6.00MB + +OK (1 test, 6 assertions) +``` + +--- + +[.build-lists: false] + +## _Tasks_ + +- ~~Ensure the blog page exists~~ +- ~~Ensure only published articles are shown~~ +- Ensure the articles are shown in the correct order + +--- + +### _Step 4_ +## Ensure the articles are ordered by date + +--- + + +```php +// modules/custom/tdd_blog/tests/src/Kernel/BlogPageTest.php + +public function testArticlesAreOrderedByDate() { + // Given that I have numerous articles with different post dates. + + // When I go to the blog page. + + // The articles are ordered by post date. +} +``` + +--- + + +```php, [.highlight: 4-9] +// modules/custom/tdd_blog/tests/src/Kernel/BlogPageTest.php + +public function testArticlesAreOrderedByDate() { + // Given that I have numerous articles with different post dates. + $this->createNode(['type' => 'article', 'created' => (new \DateTime())->modify('+1 day')->getTimestamp()]); + $this->createNode(['type' => 'article', 'created' => (new \DateTime())->modify('+1 month')->getTimestamp()]); + $this->createNode(['type' => 'article', 'created' => (new \DateTime())->modify('+3 days')->getTimestamp()]); + $this->createNode(['type' => 'article', 'created' => (new \DateTime())->modify('+1 hour')->getTimestamp()]); + + // When I go to the blog page. + + // The articles are ordered by post date. +} +``` + +--- + +```php +$this->createNode([ + 'type' => 'article', + 'created' => (new \DateTime())->modify('+1 day')->getTimestamp(), +]); +``` + +^ Array of default values + +--- + + +```php, [.highlight: 10-11] +// modules/custom/tdd_blog/tests/src/Kernel/BlogPageTest.php + +public function testArticlesAreOrderedByDate() { + // Given that I have numerous articles with different post dates. + $this->createNode(['type' => 'article', 'created' => (new \DateTime())->modify('+1 day')->getTimestamp()]); + $this->createNode(['type' => 'article', 'created' => (new \DateTime())->modify('+1 month')->getTimestamp()]); + $this->createNode(['type' => 'article', 'created' => (new \DateTime())->modify('+3 days')->getTimestamp()]); + $this->createNode(['type' => 'article', 'created' => (new \DateTime())->modify('+1 hour')->getTimestamp()]); + + // When I go to the blog page. + $results = views_get_view_result('blog'); + + // The articles are ordered by post date. +} +``` + +--- + + +```php, [.highlight:10-15] +// modules/custom/tdd_blog/tests/src/Kernel/BlogPageTest.php + +public function testArticlesAreOrderedByDate() { + // Given that I have numerous articles with different post dates. + $this->createNode(['type' => 'article', 'created' => (new \DateTime())->modify('+1 day')->getTimestamp()]); + $this->createNode(['type' => 'article', 'created' => (new \DateTime())->modify('+1 month')->getTimestamp()]); + $this->createNode(['type' => 'article', 'created' => (new \DateTime())->modify('+3 days')->getTimestamp()]); + $this->createNode(['type' => 'article', 'created' => (new \DateTime())->modify('+1 hour')->getTimestamp()]); + + // When I go to the blog page. + $results = views_get_view_result('blog'); + + $nids = array_map(function(ResultRow $result) { + return $result->_entity->id(); + }, $results); + + // The articles are ordered by post date. +} +``` + +--- + + +```php, [.highlight: 5-9, 17-18] +// modules/custom/tdd_blog/tests/src/Kernel/BlogPageTest.php + +public function testArticlesAreOrderedByDate() { + // Given that I have numerous articles with different post dates. + $this->createNode(['type' => 'article', 'created' => (new \DateTime())->modify('+1 day')->getTimestamp()]); + $this->createNode(['type' => 'article', 'created' => (new \DateTime())->modify('+1 month')->getTimestamp()]); + $this->createNode(['type' => 'article', 'created' => (new \DateTime())->modify('+3 days')->getTimestamp()]); + $this->createNode(['type' => 'article', 'created' => (new \DateTime())->modify('+1 hour')->getTimestamp()]); + + // When I go to the blog page. + $results = views_get_view_result('blog'); + + $nids = array_map(function(ResultRow $result) { + return $result->_entity->id(); + }, $results); + + // The articles are ordered by post date. + $this->assertEquals([4, 1, 3, 2], $nids); +} +``` + +--- + + +``` +PHPUnit 6.5.8 by Sebastian Bergmann and contributors. + +Testing web/modules/custom/tdd_blog/tests/src/Kernel +F 1 / 1 (100%) + +Time: 1.42 seconds, Memory: 6.00MB + +There was 1 failure: + +1) Drupal\Tests\tdd_blog\Kernel\BlogPageTest::testArticlesAreOrderedByDate +Failed asserting that two arrays are equal. +--- Expected ++++ Actual +@@ @@ + Array ( +- 0 => 4 +- 1 => 1 +- 2 => 3 +- 3 => 2 ++ 0 => '3' ++ 1 => '2' ++ 2 => '4' ++ 3 => '1' + +/Users/opdavies/Code/drupal-testing-workshop/web/core/tests/Drupal/KernelTests/KernelTestBase.php:1114 +/Users/opdavies/Code/drupal-testing-workshop/web/modules/custom/tdd_blog/tests/src/Kernel/BlogPageTest.php:43 + +FAILURES! +Tests: 1, Assertions: 4, Failures: 1. +``` + +--- + +[.text: comic sans] + +```[.highlight: 8-26] +PHPUnit 6.5.8 by Sebastian Bergmann and contributors. + +Testing web/modules/custom/tdd_blog/tests/src/Kernel +F 1 / 1 (100%) + +Time: 1.42 seconds, Memory: 6.00MB + +There was 1 failure: + +1) Drupal\Tests\tdd_blog\Kernel\BlogPageTest::testArticlesAreOrderedByDate +Failed asserting that two arrays are equal. +--- Expected ++++ Actual +@@ @@ + Array ( +- 0 => 4 +- 1 => 1 +- 2 => 3 +- 3 => 2 ++ 0 => '3' ++ 1 => '2' ++ 2 => '4' ++ 3 => '1' + +/Users/opdavies/Code/drupal-testing-workshop/web/core/tests/Drupal/KernelTests/KernelTestBase.php:1114 +/Users/opdavies/Code/drupal-testing-workshop/web/modules/custom/tdd_blog/tests/src/Kernel/BlogPageTest.php:43 + +FAILURES! +Tests: 1, Assertions: 4, Failures: 1. +``` + +--- + +- _There is no sort order defined on the view_ +- Add the sort order +- Re-export the view + +--- + +```[.highlight:3-8] +PHPUnit 6.5.8 by Sebastian Bergmann and contributors. + +Testing web/modules/custom/tdd_blog/tests/src/Kernel +. 1 / 1 (100%) + +Time: 1.74 seconds, Memory: 6.00MB + +OK (1 test, 5 assertions) +``` + +--- + +[.build-lists: false] + +## _Tasks_ + +- ~~Ensure the blog page exists~~ +- ~~Ensure only published articles are shown~~ +- ~~Ensure the articles are shown in the correct order~~ + +--- + +[.header: alignment(center)] + +# Questions? + +--- + +[.header: alignment(center)] + +# Thanks +### _@opdavies_ +### _oliverdavies.uk_ diff --git a/drupal-testing-workshop/2018-06-28-drupal-bristol/slides.pdf b/drupal-testing-workshop/2018-06-28-drupal-bristol/slides.pdf new file mode 100644 index 0000000..c19b548 Binary files /dev/null and b/drupal-testing-workshop/2018-06-28-drupal-bristol/slides.pdf differ diff --git a/drupal-testing-workshop/2018-09-19-microserve/drupal-testing-workshop-microserve.pdf b/drupal-testing-workshop/2018-09-19-microserve/drupal-testing-workshop-microserve.pdf new file mode 100644 index 0000000..e4dec79 Binary files /dev/null and b/drupal-testing-workshop/2018-09-19-microserve/drupal-testing-workshop-microserve.pdf differ diff --git a/drupal-testing-workshop/2018-09-19-microserve/drupal-testing-workshop-microserve2.pdf b/drupal-testing-workshop/2018-09-19-microserve/drupal-testing-workshop-microserve2.pdf new file mode 100644 index 0000000..6675d9f Binary files /dev/null and b/drupal-testing-workshop/2018-09-19-microserve/drupal-testing-workshop-microserve2.pdf differ diff --git a/drupal-testing-workshop/2018-09-19-microserve/images/tdd-blog-1.png b/drupal-testing-workshop/2018-09-19-microserve/images/tdd-blog-1.png new file mode 100644 index 0000000..ea4fdc0 Binary files /dev/null and b/drupal-testing-workshop/2018-09-19-microserve/images/tdd-blog-1.png differ diff --git a/drupal-testing-workshop/2018-09-19-microserve/images/tdd-blog-2.png b/drupal-testing-workshop/2018-09-19-microserve/images/tdd-blog-2.png new file mode 100644 index 0000000..3bce812 Binary files /dev/null and b/drupal-testing-workshop/2018-09-19-microserve/images/tdd-blog-2.png differ diff --git a/drupal-testing-workshop/2018-09-19-microserve/images/tdd-blog-3.png b/drupal-testing-workshop/2018-09-19-microserve/images/tdd-blog-3.png new file mode 100644 index 0000000..3b20e02 Binary files /dev/null and b/drupal-testing-workshop/2018-09-19-microserve/images/tdd-blog-3.png differ diff --git a/drupal-testing-workshop/2018-09-19-microserve/images/tdd-blog-4.png b/drupal-testing-workshop/2018-09-19-microserve/images/tdd-blog-4.png new file mode 100644 index 0000000..66a0b80 Binary files /dev/null and b/drupal-testing-workshop/2018-09-19-microserve/images/tdd-blog-4.png differ diff --git a/drupal-testing-workshop/2018-09-19-microserve/images/tdd-blog-5.png b/drupal-testing-workshop/2018-09-19-microserve/images/tdd-blog-5.png new file mode 100644 index 0000000..b50dd8a Binary files /dev/null and b/drupal-testing-workshop/2018-09-19-microserve/images/tdd-blog-5.png differ diff --git a/drupal-testing-workshop/2018-09-19-microserve/slides.md b/drupal-testing-workshop/2018-09-19-microserve/slides.md new file mode 100644 index 0000000..7f23a21 --- /dev/null +++ b/drupal-testing-workshop/2018-09-19-microserve/slides.md @@ -0,0 +1,1368 @@ +autoscale: true +build-lists: true +header-emphasis: #3D85C6 +header: alignment(left) +text: alignment(left) +text-emphasis: #3D85C6 +theme: poster, 8 +code: Monaco, #6699FF, #999999, #6666FF, #66FF66, #66FF66, line-height(1.5) + +[.header: alignment(center)] + +![](../images/title.png) + +# [fit] Drupal Testing Workshop +### _September 2018_ + +--- + +[.header: alignment(center)] + +## _Why_ write tests? + +--- + +## _Why write tests?_ + +- Catch bugs earlier +- Peace of mind +- Prevent regressions +- Write less code +- Documentation +- Drupal core requirement - __ +- More important with regular D8 releases + +^ Dave Liddament talk - better and cheaper to catch bugs earlier (e.g. whilst developing rather than after it's been released) +Refer to tests when writing implementation code +ONO merge conflict + +--- + +[.header: alignment(center)] + +## [fit] _Having tests does not mean_ +## [fit] there will be no bugs + +--- + +[.header: alignment(center)] + +## [fit] _Testing may add time now_ +## [fit] but save more time in the future + +--- + +## _Testing in Drupal_ + +- _Drupal 7_ - Simpletest (testing) module provided as part of core +- _Drupal 8_ - PHPUnit added as a core dependency +- _PHPUnit Initiative_ - Simpletest to be deprecated and removed + +^ Focussing on PHPUnit today + +--- + +[.header: #3D85C6] + +## Writing Tests (Drupal 8) + +- PHP class with _.php_ extension +- _tests/src_ directory within each module +- Within the *Drupal\Tests\module_name* namespace +- Class name must match the filename +- Namespace must match the directory structure +- One test class per feature +- Each method must start with _test_ + +^ Different to D7 + +--- + +### _Exercise 1_ +## Local site setup + +--- + +[.header: #3D85C6] + +## Docksal + +- Docker based local development environment +- Microserve standard +- Open source +- Per site configuration and customisation +- fin CLI, Apache, MySQL, Solr, Varnish, Mailhog, PHPMyAdmin etc +- Virtualbox or native Docker +- Can slow down tests +- Provides consistency + +--- + +- https://github.com/opdavies/drupal-testing-workshop +- https://docksal.io/installation +- git clone +- fin init +- http://drupaltest.docksal + +^ Contains Drupal 8 with Composer, examples module + +--- + +### _Exercise 2_ +## Running Tests + +--- + +### _Option 1_ +## Simpletest module (UI) + +--- + +![fit](../images/d8-simpletest-1.png) + +--- + +![fit](../images/d8-simpletest-2.png) + +--- + +![fit](../images/d8-simpletest-3.png) + +--- + +![fit](../images/d8-simpletest-4.png) + +--- + +![fit](../images/d8-simpletest-5.png) + +--- + +![fit](../images/d8-simpletest-6.png) + +--- + +![fit](../images/d8-simpletest-7.png) + +--- + +### _Option 2_ +## Command line + +--- + +## Prerequisite _(creating a phpunit.xml file)_ + +- Configures PHPUnit +- Needed to run some types of tests +- Ignored by Git by default +- Copy _core/phpunit.xml.dist_ to _core/phpunit.xml_ +- Add and change as needed + - `SIMPLETEST_BASE_URL`, `SIMPLETEST_DB`, `BROWSERTEST_OUTPUT_DIRECTORY` + - `stopOnFailure="true"` + +--- + +``` +cd web + +../vendor/bin/phpunit -c core \ +modules/contrib/examples/phpunit_example +``` + +--- + +``` +cd web/core + +../../vendor/bin/phpunit \ +../modules/contrib/examples/phpunit_example +``` + +--- + +## Pro-tip: Add paths to _$PATH_ + +```bash +# ~/.zshrc + +export PATH=$HOME/bin:/usr/local/bin:$PATH + +export PATH=vendor/bin:$PATH +export PATH=../vendor/bin:$PATH +export PATH=node_modules/.bin:$PATH +``` + +--- + +### _Option 2_ +## CLI with Docksal + +--- + +``` +fin bash + +cd web + +../vendor/bin/phpunit -c core \ +modules/contrib/examples/phpunit_example +``` + +--- + +``` +fin bash + +cd web/core + +../../vendor/bin/phpunit \ +../modules/contrib/examples/phpunit_example +``` + +--- + +### _Option 3_ +## Docksal PHPUnit addon + +--- + +- Custom Docksal command +- Submitted to the Docksal addons repo +- _fin addon install phpunit_ +- Wrapper around phpunit command +- Copies a stub phpunit.xml file if exists, or duplicates phpunit.xml.dist +- Shorter command, combines two actions + +^ Checks for core/phpunit.xml on each test run +Will create one if is not present + +--- + +``` +fin phpunit web/modules/contrib/examples/phpunit_example +``` + +--- + +``` +Copying stubs from /var/www/.docksal/addons/phpunit/stubs +``` + +--- + +``` +PHPUnit 6.5.8 by Sebastian Bergmann and contributors. + +Testing web/modules/contrib/examples/phpunit_example +.................................. 34 / 34 (100%) + +Time: 46.8 seconds, Memory: 6.00MB + +OK (34 tests, 41 assertions) +``` + +--- + +``` +fin phpunit web/modules/contrib/examples/phpunit_example + + +Copying /var/www/web/core/phpunit.xml.dist to /var/www/web/core/phpunit.xml. +Please edit it's values as needed and re-run 'fin phpunit'. +``` +--- + +``` +fin phpunit web/modules/contrib/examples/phpunit_example + + +PHPUnit 6.5.8 by Sebastian Bergmann and contributors. + +Testing web/modules/contrib/examples/phpunit_example +.................................. 34 / 34 (100%) + +Time: 48.62 seconds, Memory: 6.00MB + +OK (34 tests, 41 assertions) +``` + +--- + +### _Option 4_ +## IDE/text editor integration + +--- + +![fit](../images/phpstorm-integration.png) + +--- + +[.header: alignment(center)] + +## Types of tests + +--- + +[.header: #3D85C6] + +## Functional tests + +- Tests functionality +- Interacts with database +- Full Drupal installation +- With/without JavaScript + +^ testing profile + +--- + + + +[.header: #3D85C6] + +## Functional tests + +- Slower to run +- Easiest to start with +- Provide most value + +^ Less setup steps +No mocking etc. + +--- + +### _Exercise_ +## Let's write a
functional test + +--- + +- Create a _web/modules/custom/workshop_ directory +- Create a `workshop.info.yml` file + +--- + +``` +# workshop.info.yml + +name: Drupal Testing Workshop +core: 8.x +type: module +``` + +--- + +- Create a _tests/src/Functional_ directory +- Create an _ExampleFunctionalTest.php_ file + +--- + + +```php +// ExampleFunctionalTest.php + +namespace Drupal\Tests\workshop\Functional; + +use Drupal\Tests\BrowserTestBase; + +class ExampleFunctionalTest extends BrowserTestBase { + +} + +``` + +--- + +```php + +// ExampleFunctionalTest.php + +public function testExamplePageExists() { + +} +``` + +--- + +```php + +// ExampleFunctionalTest.php + +public function test_example_page_exists() { + +} +``` + +^ Snake case test method names +Still works because it has the 'test' prefix +More readable than camel case? +Works with Simpletest/D7 + +--- + +```php + +// ExampleFunctionalTest.php + +/** @test */ +public function example_page_exists() { + +} +``` + +^ Remove the prefix, use annotation +PHPUnit only + +--- + +```php + +// ExampleFunctionalTest.php + +/** @test */ +public function example_page_exists() { + // Arrange + + // Act + + // Assert +} +``` + +--- + +```php + +// ExampleFunctionalTest.php + +/** @test */ +public function example_page_exists() { + // Given that I am an anonymous user. + + // When I go to /blog. + + // I should see the blog page. +} +``` + +--- + +```php +// ExampleFunctionalTest.php + +/** @test */ +public function example_page_exists() { + $this->drupalGet('/example-one'); + + $this->assertSession()->statusCodeEquals(200); +} +``` + +--- + +```php +// ExampleFunctionalTest.php + +protected static $modules = ['workshop']; +``` + +--- + +``` +PHPUnit 6.5.8 by Sebastian Bergmann and contributors. + +Testing Drupal\Tests\workshop\Functional\ExampleFunctionalTest + +Behat\Mink\Exception\ExpectationException : Current response status code is 404, but 200 expected. + /var/www/vendor/behat/mink/src/WebAssert.php:768 + /var/www/vendor/behat/mink/src/WebAssert.php:130 + /var/www/web/modules/custom/workshop/tests/src/Functional/ExampleFunctionalTest.php:14 + +Time: 18.2 seconds, Memory: 6.00MB + +ERRORS! +Tests: 1, Assertions: 2, Errors: 1. +``` + +--- + +- Create a _workshop.routing.yml_ file +- Create a Controller + +--- + +```yaml +# workshop.routing.yml + +workshop.example: + path: '/example-one' + defaults: + _controller: 'Drupal\workshop\Controller\ExampleController::index' + requirements: + _access: 'TRUE' +``` + +--- + +```php +// src/Controller/ExampleController.php + +namespace Drupal\workshop\Controller; + +class ExampleController { + + public function index() { + return []; + } + +} +``` + +--- + +``` +Time: 25.22 seconds, Memory: 6.00MB + +OK (1 test, 3 assertions) +``` + +--- + +[.header: #3D85C6] + +## Kernel tests + +- Integration tests +- Can install modules, interact with services, container, database +- Minimal Drupal bootstrap +- Faster than functional tests +- More setup required + +--- + +### _Exercise_ +## Let's write a
kernel test + +--- + +- Create a _tests/src/Kernel_ directory +- Create an _ExampleKernelTest.php_ file +- Create a Service +- Use the service within the test to perform an action + +--- + +```php +// tests/src/Kernel/ExampleKernelTest.php + +namespace Drupal\Tests\workshop\Kernel; + +use Drupal\KernelTests\Core\Entity\EntityKernelTestBase; +use Drupal\user\Entity\User; + +class ExampleKernelTest extends EntityKernelTestBase { + + public static $modules = ['workshop']; + +} + +``` + +--- + +```php +// tests/src/Kernel/ExampleKernelTest.php + +public function testUserDeleter { + $user = $this->createUser(); + + $user_deleter = \Drupal::service(UserDeleter::class); + $user_deleter->delete($user); + + $user = $this->reloadEntity($user); + + $this->assertNull($user); +} +``` + +--- + +```yaml +# workshop.services.yml + +services: + workshop.user_deleter: + class: Drupal\workshop\Service\UserDeleter +``` + +--- + +```yaml +# workshop.services.yml + +services: + Drupal\workshop\Service\UserDeleter: ~ +``` + +--- + +``` +Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException : +You have requested a non-existent service +"Drupal\Tests\workshop\Kernel\UserDeleter". +``` + +--- + +```php +// src/Service/UserDeleter.php + +namespace Drupal\workshop\Service; + +use Drupal\Core\Session\AccountInterface; + +class UserDeleter { + + public function delete(AccountInterface $user) { + user_delete($user->id()); + } + +} +``` + +--- + +```php +// tests/src/Kernel/ExampleKernelTest.php + +namespace Drupal\Tests\workshop\Kernel; + +use Drupal\workshop\Service\UserDeleter; +use Drupal\KernelTests\Core\Entity\EntityKernelTestBase; +use Drupal\user\Entity\User; + +... +``` + +^ Import new UserDeleter + +--- + +``` +Drupal\Core\Entity\EntityStorageException : SQLSTATE[HY000]: +General error: 1 no such table: +test89378988.users_data: DELETE FROM {users_data} +WHERE uid IN (:db_condition_placeholder_0); Array +( + [:db_condition_placeholder_0] => 1 +) +``` + +--- + +```php +// tests/src/Kernel/ExampleKernelTest.php + +protected function setUp() { + parent::setUp(); + + $this->installSchema('user', ['users_data']); +} +``` + +--- + +``` +OK (1 test, 5 assertions) +``` + +--- + +[.header: #3D85C6] + +## Unit tests + +- Tests PHP logic +- No database interaction +- Fast to run +- Tightly coupled +- Mocking dependencies +- Hard to refactor + +--- + +### _Exercise_ +## Let's write a
unit test + +--- + +```php +// tests/src/Unit/Service/ExampleUnitTest.php + +namespace Drupal\Tests\workshop\Unit; + +use Drupal\Tests\UnitTestCase; + +class ExampleUnitTest extends UnitTestCase { + + public function testAdd() { + $this->assertEquals(5, (new Calculator(3))->add(2)->calculate()); + } +} +``` + +--- + +``` +Error : Class 'Drupal\Tests\workshop\Unit\Calculator' not found + /var/www/web/modules/custom/workshop/tests/src/Unit/Service/ExampleUnitTest.php:10 +``` + +--- + +```php +// src/Service/Calculator.php + +namespace Drupal\workshop\Service; + +class Calculator { + + private $total; + + public function __construct($value) { + $this->total = $value; + } + + public function add($value) { + $this->total += $value; + + return $this; + } + + public function calculate() { + return $this->total; + } + +} +``` + +--- + +```php +// tests/src/Unit/Service/ExampleUnitTest.php + +namespace Drupal\Tests\workshop\Unit; + +use Drupal\workshop\Service\Calculator; +use Drupal\Tests\UnitTestCase; + +... +``` + +--- + +``` +Time: 4.55 seconds, Memory: 4.00MB + +OK (1 test, 1 assertion) +``` + +--- + +[.header: alignment(center)] + +## Test driven
development _(TDD)_ + +--- + + +## _Test Driven Development_ + +- Write a failing test +- Write code until passes +- Refactor +- Repeat + +--- + +[.background-color: #FFFFFF] +[.footer: https://github.com/foundersandcoders/testing-tdd-intro] +[.footer-style: #2F2F2F] + +![100%](../images/tdd-loop.png) + +--- + +## _How I Write Tests - "Outside In"_ + +- Start with functional tests +- Drop down to kernel or unit tests where needed +- Programming by wishful thinking +- Write comments first, then fill in the code +- Sometimes write assertions first + +--- + +### _Exercise_ +## Let's build a blog using test driven development + + +--- + +## _Acceptance criteria_ + +- As a site visitor +- I want to see a list of published articles at /blog +- Ordered by post date + +--- + +## _Tasks_ + +- Ensure the blog page exists +- Ensure only published articles are shown +- Ensure the articles are shown in the correct order + +--- + +## _Implementation_ + +- Use views module +- Do the mininum amount at each step, make no assumptions, let the tests guide us +- Start with functional test + +--- + +### _Step 1_ +## Create the module + +--- + +```yml +# tdd_blog.info.yml + +name: 'TDD Blog' +core: '8.x' +type: 'module' +``` + +--- + +### _Step 2_ +## Ensure the blog page exists + +--- + +```php +// tests/src/Functional/BlogPageTest.php + +namespace Drupal\Tests\tdd_blog\Functional; + +use Drupal\Tests\BrowserTestBase; + +class BlogPageTest extends BrowserTestBase { + + protected static $modules = ['tdd_blog']; + +} +``` + +--- + + +```php +public function testBlogPageExists() { + $this->drupalGet('/blog'); + + $this->assertSession()->statusCodeEquals(200); +} +``` + +--- + + +``` +There was 1 error: + +1) Drupal\Tests\tdd_blog\Functional\BlogPageTest::testBlogPageExists +Behat\Mink\Exception\ExpectationException: Current response status code is 404, but 200 expected. +``` + +--- + +- _The view has not been created_ +- Create a new view, page display +- Set the path +- Export the config +- Copy it into the module's `config/install` directory + +--- + +![fit](images/tdd-blog-1.png) + +--- + +![fit](images/tdd-blog-2.png) + +--- + +``` +drush cex -y + +cp ../config/default/views.view.blog.yml \ + modules/custom/tdd_blog/config/install +``` + +--- + +```diff +# views.view.blog.yml + +- uuid: 84305edf-7aef-4109-bc93-e87f685fb678 +langcode: en +status: true +dependencies: + config: + - node.type.article + module: + - node + - user +- _core: +- default_config_hash: iGZkqLWpwWNORq6_fy6v_Kn_KE4BjYHqj9vpgQsWJCs +id: blog +... +``` + +--- + +``` +1) Drupal\Tests\tdd_blog\Functional\BlogPageTest::testBlogPageExists +Drupal\Core\Config\UnmetDependenciesException: Configuration objects provided +by tdd_blog +have unmet dependencies: views.view.blog +(node.type.article, node, views) +``` + +--- + + +```yml,[.highlight: 1, 7-10] +# tdd_blog.info.yml + +name: 'TDD Blog' +description: 'A demo module to show test driven module development.' +core: 8.x +type: module + +dependencies: + - 'drupal:node' + - 'drupal:views' +``` + +--- + + +``` +1) Drupal\Tests\tdd_blog\Functional\BlogPageTest::testBlogPageExists +Drupal\Core\Config\UnmetDependenciesException: Configuration objects provided +by tdd_blog have unmet dependencies: +views.view.blog (node.type.article) +``` + +--- + +- Add the article content type + +--- + + +``` +OK (1 test, 3 assertions) +``` + +--- + +[.build-lists: false] + +## _Tasks_ + +- ~~Ensure the blog page exists~~ +- Ensure only published articles are shown +- Ensure the articles are shown in the correct order + +--- + +### _Step 3_ +## Ensure only published articles are shown + +--- + + +```php +public function testOnlyPublishedArticlesAreShown() { + // Given I have a mixture of published and unpublished articles, + // as well as other types of content. + + // When I view the blog page. + + // I should only see the published articles. +} +``` + +--- + +### _Option 1_ +## Functional tests + +--- + + +```php +// modules/custom/tdd_blog/tests/src/Functional/BlogPageTest.php + +public function testOnlyPublishedArticlesAreShown() { + // Given I have a mixture of published and unpublished articles, + // as well as other types of content. + $node1 = $this->drupalCreateNode(['type' => 'page', 'status' => 1]); + $node2 = $this->drupalCreateNode(['type' => 'article', 'status' => 1]); + $node3 = $this->drupalCreateNode(['type' => 'article', 'status' => 0]); + + // When I view the blog page. + $this->drupalGet('/blog'); + + // I should only see the published articles. + $assert = $this->assertSession(); + $assert->pageTextContains($node2->label()); + $assert->pageTextNotContains($node1->label()); + $assert->pageTextNotContains($node3->label()); +} +``` + +^ Different ways to achieve this. This is taking the functional test approach. + +--- + +### _Option 2_ +## Kernel tests + +--- + + +```php +namespace Drupal\Tests\tdd_blog\Kernel; + +use Drupal\KernelTests\Core\Entity\EntityKernelTestBase; +use Drupal\Tests\node\Traits\NodeCreationTrait; + +class BlogPageTest extends EntityKernelTestBase { + + use NodeCreationTrait; + + public static $modules = ['node']; + +} +``` + +--- + +```php + +public function testOnlyPublishedArticlesAreShown() { + $this->createNode(['type' => 'page', 'status' => 1]); + $this->createNode(['type' => 'article', 'status' => 1]); + $this->createNode(['type' => 'article', 'status' => 0]); +} +``` + +^ Kernel test approach +Dropping down a level +No need for the brower, not asserting against HTML +Faster to run + +--- + +``` +1) Drupal\Tests\tdd_blog\Kernel\BlogPageTest::testOnlyPublishedArticlesAreShown +Error: Call to a member function id() on boolean + +/var/www/web/core/modules/filter/filter.module:212 +/var/www/web/core/modules/node/tests/src/Traits/NodeCreationTrait.php:73 +/var/www/web/modules/custom/tdd_blog/tests/src/Kernel/BlogPageTest.php:13 +``` + +--- + +```php +$this->installConfig(['filter']); +``` + +--- + +```php +public function testOnlyPublishedArticlesAreShown() { + ... + + $results = views_get_view_result('blog'); +} +``` + +--- + +```php +public function testOnlyPublishedArticlesAreShown() { + ... + + $results = views_get_view_result('blog'); + + $this->assertCount(1, $results); + $this->assertEquals(2, $results[0]->_entity->id()); +} +``` + +--- + +```php +public static $modules = [ + 'node', + 'tdd_blog', + 'views', +]; +``` + +--- + +```php +public function setUp() { + parent::setUp(); + + $this->installConfig(['filter', 'tdd_blog']); +} +``` + +--- + +``` +There was 1 failure: + +1) Drupal\Tests\tdd_blog\Kernel\BlogPageTest::testOnlyPublishedArticlesAreShown +Failed asserting that actual size 2 matches expected size 1. + +/Users/opdavies/Code/drupal-testing-workshop/web/modules/custom/tdd_blog/tests/src/Kernel/BlogPageTest.php:23 +``` + +--- + +![inline](images/tdd-blog-3.png) + +--- + +>- _There is no content type filter on the view_ +- Add the filter +- Re-export and save the view + +--- + +![inline](images/tdd-blog-4.png) + +--- + +``` +OK (1 test, 6 assertions) +``` + +--- + +[.build-lists: false] + +## _Tasks_ + +- ~~Ensure the blog page exists~~ +- ~~Ensure only published articles are shown~~ +- Ensure the articles are shown in the correct order + +--- + +### _Step 4_ +## Ensure the articles are ordered by date + +--- + + +```php +// modules/custom/tdd_blog/tests/src/Kernel/BlogPageTest.php + +public function testArticlesAreOrderedByDate() { + // Given that I have numerous articles with different post dates. + + // When I go to the blog page. + + // The articles are ordered by post date. +} +``` + +--- + + +```php +// modules/custom/tdd_blog/tests/src/Kernel/BlogPageTest.php + +public function testArticlesAreOrderedByDate() { + // Given that I have numerous articles with different post dates. + $this->createNode(['type' => 'article', 'created' => (new DrupalDateTime('+1 day'))->getTimestamp()]); + $this->createNode(['type' => 'article', 'created' => (new DrupalDateTime('+1 month'))->getTimestamp()]); + $this->createNode(['type' => 'article', 'created' => (new DrupalDateTime('+3 days'))->getTimestamp()]); + $this->createNode(['type' => 'article', 'created' => (new DrupalDateTime('+1 hour'))->getTimestamp()]); + + // When I go to the blog page. + + // The articles are ordered by post date. +} +``` + +--- + +```php +$this->createNode([ + 'type' => 'article', + 'created' => (new DrupalDateTime('+1 day'))->getTimestamp(), +]); +``` + +^ Array of default values + +--- + + +```php +// modules/custom/tdd_blog/tests/src/Kernel/BlogPageTest.php + +public function testArticlesAreOrderedByDate() { + ... + + // When I go to the blog page. + $results = views_get_view_result('blog'); + + // The articles are ordered by post date. +} +``` + +--- + +```php +// modules/custom/tdd_blog/tests/src/Kernel/BlogPageTest.php + +public function testArticlesAreOrderedByDate() { + ... + + // When I go to the blog page. + $results = views_get_view_result('blog'); + + $nids = array_map(function(ResultRow $result) { + return $result->_entity->id(); + }, $results); + + // The articles are ordered by post date. +} +``` + +--- + + +```php +// modules/custom/tdd_blog/tests/src/Kernel/BlogPageTest.php + +public function testArticlesAreOrderedByDate() { + ... + + // The articles are ordered by post date. + $this->assertEquals([4, 1, 3, 2], $nids); +} +``` + +--- + + +``` +There was 1 failure: + +1) Drupal\Tests\tdd_blog\Kernel\BlogPageTest::testArticlesAreOrderedByDate +Failed asserting that two arrays are equal. +--- Expected ++++ Actual +@@ @@ + Array ( +- 0 => 4 +- 1 => 1 +- 2 => 3 +- 3 => 2 ++ 0 => '1' ++ 1 => '2' ++ 2 => '3' ++ 3 => '4' +``` + +--- + +![inline](images/tdd-blog-4.png) + +--- + +- _There is no sort order defined on the view_ +- Add the sort order +- Re-export the view + +--- + +![inline](images/tdd-blog-5.png) + +--- + +``` +OK (1 test, 5 assertions) +``` + +--- + +[.build-lists: false] + +## _Tasks_ + +- ~~Ensure the blog page exists~~ +- ~~Ensure only published articles are shown~~ +- ~~Ensure the articles are shown in the correct order~~ + +--- + +[.header: alignment(center)] + +## Take Aways + +--- + +- Testing has made me a _better developer_ +- Testing can produce _better quality code_ +- Use the _right type of test_ for the right situation +- Use the _right base class_, use available _traits_ +- Writing tests is an _investment_ +- OK to _start small_, introduce tests gradually +- Easier to _refactor_ +- Tests can pass, but things can _still be broken_. Tests only report on what they cover. + +^ Made me think about how I'm going to do something more starting to do it +Less cruft, only write code that serves a purpose +Spending time writing tests pays dividends later on +Start by introducing tests for new features or regression tests when fixing bugs +If you know things pass, then you can refactor code knowing if something is broken +Manual testing is still important + +--- + +[.header: alignment(center)] + +## Questions? + +--- + +[.header: alignment(center)] + +## Thanks diff --git a/drupal-testing-workshop/images/2c6qi8.jpg b/drupal-testing-workshop/images/2c6qi8.jpg new file mode 100644 index 0000000..93bd9a9 Binary files /dev/null and b/drupal-testing-workshop/images/2c6qi8.jpg differ diff --git a/drupal-testing-workshop/images/appnovation.png b/drupal-testing-workshop/images/appnovation.png new file mode 100644 index 0000000..49e432c Binary files /dev/null and b/drupal-testing-workshop/images/appnovation.png differ diff --git a/drupal-testing-workshop/images/broadbean.png b/drupal-testing-workshop/images/broadbean.png new file mode 100644 index 0000000..3aaf673 Binary files /dev/null and b/drupal-testing-workshop/images/broadbean.png differ diff --git a/drupal-testing-workshop/images/collection-class-1.png b/drupal-testing-workshop/images/collection-class-1.png new file mode 100644 index 0000000..3c1d004 Binary files /dev/null and b/drupal-testing-workshop/images/collection-class-1.png differ diff --git a/drupal-testing-workshop/images/collection-class-2.png b/drupal-testing-workshop/images/collection-class-2.png new file mode 100644 index 0000000..09be34e Binary files /dev/null and b/drupal-testing-workshop/images/collection-class-2.png differ diff --git a/drupal-testing-workshop/images/d8-simpletest-1.png b/drupal-testing-workshop/images/d8-simpletest-1.png new file mode 100644 index 0000000..5ef734a Binary files /dev/null and b/drupal-testing-workshop/images/d8-simpletest-1.png differ diff --git a/drupal-testing-workshop/images/d8-simpletest-2.png b/drupal-testing-workshop/images/d8-simpletest-2.png new file mode 100644 index 0000000..6a86356 Binary files /dev/null and b/drupal-testing-workshop/images/d8-simpletest-2.png differ diff --git a/drupal-testing-workshop/images/d8-simpletest-3.png b/drupal-testing-workshop/images/d8-simpletest-3.png new file mode 100644 index 0000000..9f2e423 Binary files /dev/null and b/drupal-testing-workshop/images/d8-simpletest-3.png differ diff --git a/drupal-testing-workshop/images/d8-simpletest-4.png b/drupal-testing-workshop/images/d8-simpletest-4.png new file mode 100644 index 0000000..162c867 Binary files /dev/null and b/drupal-testing-workshop/images/d8-simpletest-4.png differ diff --git a/drupal-testing-workshop/images/d8-simpletest-5.png b/drupal-testing-workshop/images/d8-simpletest-5.png new file mode 100644 index 0000000..2e1e4fe Binary files /dev/null and b/drupal-testing-workshop/images/d8-simpletest-5.png differ diff --git a/drupal-testing-workshop/images/d8-simpletest-6.png b/drupal-testing-workshop/images/d8-simpletest-6.png new file mode 100644 index 0000000..33e29f6 Binary files /dev/null and b/drupal-testing-workshop/images/d8-simpletest-6.png differ diff --git a/drupal-testing-workshop/images/d8-simpletest-7.png b/drupal-testing-workshop/images/d8-simpletest-7.png new file mode 100644 index 0000000..9b5463d Binary files /dev/null and b/drupal-testing-workshop/images/d8-simpletest-7.png differ diff --git a/drupal-testing-workshop/images/dcbristol.png b/drupal-testing-workshop/images/dcbristol.png new file mode 100644 index 0000000..552904f Binary files /dev/null and b/drupal-testing-workshop/images/dcbristol.png differ diff --git a/drupal-testing-workshop/images/deploy-all-the-things.jpg b/drupal-testing-workshop/images/deploy-all-the-things.jpg new file mode 100644 index 0000000..f90028e Binary files /dev/null and b/drupal-testing-workshop/images/deploy-all-the-things.jpg differ diff --git a/drupal-testing-workshop/images/files.png b/drupal-testing-workshop/images/files.png new file mode 100644 index 0000000..a23b62a Binary files /dev/null and b/drupal-testing-workshop/images/files.png differ diff --git a/drupal-testing-workshop/images/homer-smart.png b/drupal-testing-workshop/images/homer-smart.png new file mode 100644 index 0000000..50c5705 Binary files /dev/null and b/drupal-testing-workshop/images/homer-smart.png differ diff --git a/drupal-testing-workshop/images/kernel-tests.png b/drupal-testing-workshop/images/kernel-tests.png new file mode 100644 index 0000000..20921a7 Binary files /dev/null and b/drupal-testing-workshop/images/kernel-tests.png differ diff --git a/drupal-testing-workshop/images/matt-stauffer-tweet.png b/drupal-testing-workshop/images/matt-stauffer-tweet.png new file mode 100644 index 0000000..3f27a45 Binary files /dev/null and b/drupal-testing-workshop/images/matt-stauffer-tweet.png differ diff --git a/drupal-testing-workshop/images/me.jpg b/drupal-testing-workshop/images/me.jpg new file mode 100644 index 0000000..4b3e031 Binary files /dev/null and b/drupal-testing-workshop/images/me.jpg differ diff --git a/drupal-testing-workshop/images/phpstorm-integration.png b/drupal-testing-workshop/images/phpstorm-integration.png new file mode 100644 index 0000000..c0d12c7 Binary files /dev/null and b/drupal-testing-workshop/images/phpstorm-integration.png differ diff --git a/drupal-testing-workshop/images/phpunit.png b/drupal-testing-workshop/images/phpunit.png new file mode 100644 index 0000000..d22c405 Binary files /dev/null and b/drupal-testing-workshop/images/phpunit.png differ diff --git a/drupal-testing-workshop/images/simpletest-1.png b/drupal-testing-workshop/images/simpletest-1.png new file mode 100644 index 0000000..368de76 Binary files /dev/null and b/drupal-testing-workshop/images/simpletest-1.png differ diff --git a/drupal-testing-workshop/images/simpletest-2.png b/drupal-testing-workshop/images/simpletest-2.png new file mode 100644 index 0000000..f823167 Binary files /dev/null and b/drupal-testing-workshop/images/simpletest-2.png differ diff --git a/drupal-testing-workshop/images/simpletest-3.png b/drupal-testing-workshop/images/simpletest-3.png new file mode 100644 index 0000000..c413758 Binary files /dev/null and b/drupal-testing-workshop/images/simpletest-3.png differ diff --git a/drupal-testing-workshop/images/simpletest-4.png b/drupal-testing-workshop/images/simpletest-4.png new file mode 100644 index 0000000..fcce824 Binary files /dev/null and b/drupal-testing-workshop/images/simpletest-4.png differ diff --git a/drupal-testing-workshop/images/simpletest.png b/drupal-testing-workshop/images/simpletest.png new file mode 100644 index 0000000..76c497c Binary files /dev/null and b/drupal-testing-workshop/images/simpletest.png differ diff --git a/drupal-testing-workshop/images/tdd-circle-of-life.png b/drupal-testing-workshop/images/tdd-circle-of-life.png new file mode 100644 index 0000000..78e5f34 Binary files /dev/null and b/drupal-testing-workshop/images/tdd-circle-of-life.png differ diff --git a/drupal-testing-workshop/images/tdd-loop.png b/drupal-testing-workshop/images/tdd-loop.png new file mode 100644 index 0000000..ffd6078 Binary files /dev/null and b/drupal-testing-workshop/images/tdd-loop.png differ diff --git a/drupal-testing-workshop/images/timmillwood-ono.png b/drupal-testing-workshop/images/timmillwood-ono.png new file mode 100644 index 0000000..be4eda4 Binary files /dev/null and b/drupal-testing-workshop/images/timmillwood-ono.png differ diff --git a/drupal-testing-workshop/images/title.png b/drupal-testing-workshop/images/title.png new file mode 100644 index 0000000..3110bf9 Binary files /dev/null and b/drupal-testing-workshop/images/title.png differ diff --git a/drupal-testing-workshop/images/toggle-optional-fields-1.png b/drupal-testing-workshop/images/toggle-optional-fields-1.png new file mode 100644 index 0000000..8378788 Binary files /dev/null and b/drupal-testing-workshop/images/toggle-optional-fields-1.png differ diff --git a/drupal-testing-workshop/images/toggle-optional-fields-2.png b/drupal-testing-workshop/images/toggle-optional-fields-2.png new file mode 100644 index 0000000..4112014 Binary files /dev/null and b/drupal-testing-workshop/images/toggle-optional-fields-2.png differ diff --git a/drupal-testing-workshop/images/toggle-optional-fields-3.png b/drupal-testing-workshop/images/toggle-optional-fields-3.png new file mode 100644 index 0000000..8339887 Binary files /dev/null and b/drupal-testing-workshop/images/toggle-optional-fields-3.png differ diff --git a/drupal-testing-workshop/images/toggle-optional-fields-button.png b/drupal-testing-workshop/images/toggle-optional-fields-button.png new file mode 100644 index 0000000..7f8db5f Binary files /dev/null and b/drupal-testing-workshop/images/toggle-optional-fields-button.png differ diff --git a/drupal-testing-workshop/images/when-you-do-things-right.jpg b/drupal-testing-workshop/images/when-you-do-things-right.jpg new file mode 100644 index 0000000..efe0ac4 Binary files /dev/null and b/drupal-testing-workshop/images/when-you-do-things-right.jpg differ diff --git a/drupal-vm-symfony-console/abstract.md b/drupal-vm-symfony-console/abstract.md deleted file mode 100644 index 3b657c2..0000000 --- a/drupal-vm-symfony-console/abstract.md +++ /dev/null @@ -1,13 +0,0 @@ -# Drupal VM, meet Symfony Console - -The [Drupal VM CLI][0] is a command line application, built on Symfony Console and other components, that generates configuration files for Drupal VM based on personal settings and user interaction. - -After an introduction to Drupal VM itself and the Drupal VM CLI, we’ll jump into the code and see how Symfony Console applications are structured, how to write new commands, and how to integrate additional libraries like Guzzle, Twig and other Symfony components - whilst referencing code from the Drupal VM CLI project. - -_TL;DR - Come and learn about Symfony Console, with examples from a real project._ - -**Notes:** - -The Drupal VM CLI project is on GitHub at . I’ve given a couple of short talks about the project at user groups, though this will be more technical and more focussed on Console than previous versions of the talk. - -[0]: https://github.com/opdavies/drupal-vm-cli diff --git a/getting-your-data-into-drupal-8/abstract.md b/getting-your-data-into-drupal-8/abstract.md deleted file mode 100644 index 870b28f..0000000 --- a/getting-your-data-into-drupal-8/abstract.md +++ /dev/null @@ -1,7 +0,0 @@ -# Getting (Your Data) into Drupal 8 - -If you’ve moved a site from Drupal 6 to 7, the chances are that you’ve either used the upgrade path to update your old site in-place, or you built a new site from scratch and used the Migrate module from contrib to migrate your data from the old database. - -In Drupal 8, things have changed as there’s no upgrade path from Drupal 7 and the Migrate module has been moved into core, though there are still migration related modules available in contrib. - -This talk will look at the core Migrate module and how it implements Drupal 8 features such as YAML and the plugin and configuration systems, and how to write your own migrations to get your data into Drupal 8. diff --git a/git-tips-tricks/abstract.md b/git-tips-tricks/abstract.md deleted file mode 100644 index c33caa9..0000000 --- a/git-tips-tricks/abstract.md +++ /dev/null @@ -1,3 +0,0 @@ -# Git Tips and Tricks to Make Your Life Easier - -I've been using Git since 2010, for business, personal and open source projects, and in this talk, I'll share some my experiences working with Git and some of the tips and tricks that I use every day to make my life easier. diff --git a/goodbye-drush-make-hello-composer/2018-02-16-php-uk/slides.pdf b/goodbye-drush-make-hello-composer/2018-02-16-php-uk/slides3.pdf similarity index 100% rename from goodbye-drush-make-hello-composer/2018-02-16-php-uk/slides.pdf rename to goodbye-drush-make-hello-composer/2018-02-16-php-uk/slides3.pdf diff --git a/goodbye-drush-make-hello-composer/abstract.md b/goodbye-drush-make-hello-composer/abstract.md deleted file mode 100644 index 4ccbf22..0000000 --- a/goodbye-drush-make-hello-composer/abstract.md +++ /dev/null @@ -1,11 +0,0 @@ -# Goodbye Drush Make. Hello Composer! - -_Alternative title: Building Drupal websites with Composer_ - -One of the main outcomes of Drupal 8 was “getting off the island” with third-party code included in core and adopting modern best practices from the wider PHP ecosystem - including [Composer][0], PHP’s dependency manager. - -Included to manage core’s dependencies, it has also gained traction in the contrib space with the creation of the Drupal Composer project, and the Drupal Packagist and now native endpoints on Drupal.org exposing contrib project metadata. - -In this session, we’ll learn how to fully manage a Drupal 7 or Drupal 8 website including contributed modules and themes and external libraries with Composer. - -[0]: https://getcomposer.org diff --git a/history.yml b/history.yml deleted file mode 100644 index 5e1361c..0000000 --- a/history.yml +++ /dev/null @@ -1,160 +0,0 @@ ---- -- date: 05-09-2013 - event: Unified Diff - location: Cardiff, UK - talk: So, what is this Drupal thing? - -- date: 01-03-2014 - event: DrupalCamp London - location: London, UK - talk: An introduction to Git Flow - -- date: 19-08-2014 - event: Drupal Bristol - location: Bristol, UK - talk: drush make drupalbristol - -- date: 18-01-2015 - event: DrupalCamp Brighton - location: Brighton, UK - talk: "Drupal.org in 2015: What's Coming Next" - -- date: 28-02-2015 - event: DrupalCamp London - location: London, UK - talk: "Drupal.org in 2015: What's Coming Next" - -- date: 08-04-2015 - event: PHPSW - location: Bristol, UK - talk: Drupal 8 (lightning talk) - -- date: 25-07-2015 - event: DrupalCamp North - location: Sunderland, UK - talk: Test Drive Twig with Sculpin - -- date: 25-08-2015 - event: umBristol - location: Bristol, UK - talk: Dancing for Drupal - -- date: 14-10-2015 - event: PHPSW - location: Bristol, UK - talk: Building static websites with Sculpin - -- date: 05-03-2016 - event: DrupalCamp London - location: London, UK - talk: Getting Started with Drupal 8 Module Development - -- date: 08-03-2016 - event: NWDUG - location: Manchester, UK - talk: Drupal VM Generator (lightning talk) - -- date: 02-04-2016 - event: Drupal Bristol - location: Bristol, UK - talk: Drupal VM Generator - -- date: 11-06-2016 - event: PHP South Coast - location: Portsmouth, UK - talk: 'Drupal 8: Rejoining the Herd' - -- date: 23-07-2016 - event: DrupalCamp Bristol - location: Bristol, UK - talk: Drupal VM, Meet Symfony Console - -- date: 09-11-2016 - event: PHPSW - location: Bristol, UK - talk: Modern Drupal Development with Composer (lightning talk) - -- date: 17-11-2016 - event: Drupal Bristol - location: Bristol, UK - talk: Goodbye Drush Make. Hello Composer! - -- date: 18-01-2017 - event: Drupal Bristol - location: Bristol, UK - talk: Getting (Your Data) Into Drupal 8 - -- date: 08-02-2017 - event: PHPSW - location: Bristol, UK - talk: It All Started With A Patch - -- date: 04-03-2017 - event: DrupalCamp London - location: London, UK - talk: TDD - Test Driven Drupal - -- date: 04-03-2017 - event: DrupalCamp London - location: London, UK - talk: Getting (Your Data) Into Drupal 8 - -- date: 20-04-2017 - event: Nomad PHP - location: Online - talk: Deploying PHP Applications with Fabric (lightning talk) - -- date: 13-09-2017 - event: PHPSW - location: Bristol, UK - talk: Deploying PHP Applications with Fabric - -- date: 01-10-2017 - event: PHP North West - location: Manchester, UK - talk: Deploying PHP Applications with Fabric - -- date: 20-10-2017 - event: DrupalCamp Dublin - location: Dublin, Ireland - talk: Deploying Drupal with Fabric - -- date: 21-10-2017 - event: DrupalCamp Dublin - location: Dublin, Ireland - talk: TDD - Test Driven Drupal - -- date: 26-10-2017 - event: Drupal Somerset - location: Glastonbury, UK - talk: Deploying Drupal with Fabric - -- date: 22-11-2017 - event: Drupal Bristol - location: Bristol, UK - talk: TDD - Test Driven Drupal - -- date: 21-12-2017 - event: Nomad PHP - location: Online - talk: Using Laravel Collections, outside Laravel (lightning talk) - -- date: 17-01-2018 - event: Drupal Bristol - location: Bristol, UK - talk: Taking Flight with Tailwind CSS - -- date: 16-02-2018 - event: PHP UK conference - location: London, UK - talk: Goodbye Drush Make. Hello Composer! - -- date: 18-04-2018 - event: Drupal Bristol - location: Bristol, UK - talk: Having Fun with Drupal 8, PHP libraries and the Drupal.org API - -- date: 14-06-2018 - event: Drupal Somerset - location: Glastonbury, UK - talk: TDD - Test Driven Drupal diff --git a/ideas.md b/ideas.md deleted file mode 100644 index dfc4b12..0000000 --- a/ideas.md +++ /dev/null @@ -1,5 +0,0 @@ -# Talk Ideas - -- Webpack Encore - _using Webpack Encore to generate assets in your PHP/Symfony app or Drupal website._ -- Drupal 8 for PHP Developers - _a comparison of Drupal 8 to Symfony and Laravel._ -- Architecting and writing Drupal modules for sanity and reusability. diff --git a/me-microserve.jpg b/images/me-microserve.jpg similarity index 100% rename from me-microserve.jpg rename to images/me-microserve.jpg diff --git a/me-phpnw.png b/images/me-phpnw.png similarity index 100% rename from me-phpnw.png rename to images/me-phpnw.png diff --git a/images/me-precedent.jpg b/images/me-precedent.jpg new file mode 100644 index 0000000..4b3e031 Binary files /dev/null and b/images/me-precedent.jpg differ diff --git a/images/microserve-light.png b/images/microserve-light.png new file mode 100644 index 0000000..28c60a1 Binary files /dev/null and b/images/microserve-light.png differ diff --git a/me-precedent.jpg b/me-precedent.jpg deleted file mode 100644 index f56c9b5..0000000 Binary files a/me-precedent.jpg and /dev/null differ diff --git a/speaker-info.md b/speaker-info.md deleted file mode 100644 index e5ba023..0000000 --- a/speaker-info.md +++ /dev/null @@ -1,28 +0,0 @@ -# Speaker Info - -## Bio - -[Oliver Davies][1] ([@opdavies][2]) has been building websites and web applications since 2007 - primarily with Drupal, though also uses Symfony, Silex, Laravel and Sculpin. He is a Senior Developer at [Microserve][3], a Drupal core contributor and mentor, and an open source and contribution advocate. - -He regularly blogs and gives talks about web development and systems administration, maintains and contributes to various open source projects, and co-organises DrupalCamp Bristol as well as the Drupal Bristol and PHPSW user groups. - -## Photos - -- -- - -## Events I’ve Spoken At - -- DrupalCamp Brighton 2015 -- DrupalCamp Bristol 2016 -- DrupalCamp Dublin 2017 -- DrupalCamp London (2014, 2015, 2016, 2017) -- DrupalCamp North 2015 -- Nomad PHP -- PHP North West 2017 (10 year anniversary) -- PHP South Coast 2016 -- PHP UK 2018 - -[1]: https://www.oliverdavies.uk -[2]: https://twitter.com/opdavies -[3]: https://microserve.io diff --git a/static-websites-sculpin/abstract.md b/static-websites-sculpin/abstract.md deleted file mode 100644 index 78936bc..0000000 --- a/static-websites-sculpin/abstract.md +++ /dev/null @@ -1,7 +0,0 @@ -# Building Static Websites with Sculpin - -Sculpin (https://sculpin.io) is a static site generator written in PHP, and built on Symfony components. - -In this talk, I'll speak about static site generators and why you may want to use one, the background of the Sculpin project, what it is, and what is does, how it works and how we can extend it with our own custom bundles. - -Once we've done that, we'll open up a IDE and a Terminal and look at how we can build flexible, dynamic, static websites. diff --git a/taking-flight-with-tailwind-css/2018-01-17-drupal-bristol/1.png b/taking-flight-with-tailwind-css/2018-01-17-drupal-bristol/1.png deleted file mode 100644 index d371250..0000000 Binary files a/taking-flight-with-tailwind-css/2018-01-17-drupal-bristol/1.png and /dev/null differ diff --git a/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/images/1.png b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/images/1.png new file mode 100644 index 0000000..a8a2ebf Binary files /dev/null and b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/images/1.png differ diff --git a/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/images/laravel-nova.png b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/images/laravel-nova.png new file mode 100644 index 0000000..f5ba37e Binary files /dev/null and b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/images/laravel-nova.png differ diff --git a/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/images/php-south-wales.png b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/images/php-south-wales.png new file mode 100644 index 0000000..d4279a7 Binary files /dev/null and b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/images/php-south-wales.png differ diff --git a/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/images/refactoring-ui.png b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/images/refactoring-ui.png new file mode 100644 index 0000000..bf6e1a9 Binary files /dev/null and b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/images/refactoring-ui.png differ diff --git a/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/images/spatie.png b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/images/spatie.png new file mode 100644 index 0000000..1739a07 Binary files /dev/null and b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/images/spatie.png differ diff --git a/taking-flight-with-tailwind-css/2018-01-17-drupal-bristol/slides.md b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides.md similarity index 61% rename from taking-flight-with-tailwind-css/2018-01-17-drupal-bristol/slides.md rename to taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides.md index 9b45f2f..a54bd48 100644 --- a/taking-flight-with-tailwind-css/2018-01-17-drupal-bristol/slides.md +++ b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides.md @@ -1,33 +1,85 @@ +theme: poster, 8 autoscale: true build-lists: true -theme: Fira, 1 +header-emphasis: #53B0EB +header: alignment(left) +text: alignment(left) +text-emphasis: #53B0EB +code: Monaco, line-height(1.2) [.hide-footer] +[.header: alignment(center)] -# [fit] _**Taking Flight with**
Tailwind CSS_ +## _Taking Flight with_
Tailwind CSS --- +[.background-color: #FFFFFF] [.build-lists: false] +[.header: #111111] +[.text: #111111, alignment(left)] -- Full Stack Web Developer -- Acquia certified Drupal specialist +![right 800%](../../images/me-phpnw.png) + +- Full Stack Web Developer & System Administrator +- Senior Developer at Microserve +- Part-time freelancer +- Acquia certified Drupal 8 Grand Master +- Drupal 7 & 8 core contributor +- Symfony, Laravel, ~~Silex,~~ Sculpin - @opdavies -- oliverdavies.uk +- www.oliverdavies.uk + +^ Work at Microserve. +Maintain Drupal modules, PHP CLI tools and libraries +Blog on my website --- -> A Utility-First CSS Framework for Rapid UI Development +[.background-color: #FFFFFF] +[.build-lists: false] +[.text: #111111, alignment(left)] + +![right 100%](../../images/microserve-light.png) + +- https://microserve.io +- https://www.drupal.org/microserve +- https://github.com/microserve-io +- https://twitter.com/microserveltd +- https://www.linkedin.com/company/microserve-ltd + +--- + +[.header: alignment(center)] + +## A _Utility-First CSS Framework_ for Rapid UI Development ^ What is Tailwind? --- +[.header: alignment(center)] + + +## A CSS framework
_with no CSS_ + +--- + + + +[.header: alignment(center)] + + +## _A different way_
to write CSS + +--- + [.build-lists: false] +- Developed by Adam Wathan and others - Utility CSS class generator - PostCSS -- Configurable +- Configured with JavaScript - Some preprocessor features - Can be used in addition to preprocessors - Open source @@ -55,8 +107,8 @@ Can use features like variables if using plain CSS. font-size: 1.5rem; } -.bg-blue { - background-color: #3490DC; +.bg-red { + background-color: #93140B; } .text-white { @@ -67,8 +119,8 @@ Can use features like variables if using plain CSS. --- ```html -
-

Hello, Drupal Bristol!

+
+

Hello, PHP South Wales!

``` @@ -78,11 +130,15 @@ Can use features like variables if using plain CSS. --- -## __Installation__ +[.header: alignment(center)] + +# Installation --- -## Option 1 +[.header: #53B0EB] + +# Option 1 ``` https://cdn.jsdelivr.net/npm/tailwindcss/dist/tailwind.min.css @@ -90,12 +146,16 @@ https://cdn.jsdelivr.net/npm/tailwindcss/dist/tailwind.min.css --- -## Option 2 +[.header: #53B0EB] + +# Option 2 ```bash -yarn add postcss tailwindcss --save +npm install tailwindcss --save-dev -yarn run tailwind init +yarn add tailwindcss --dev + +./node_modules/.bin/tailwind init [filename] ``` ^ Install with npm or Yarn @@ -123,27 +183,27 @@ var colors = { --- -```js -// postcss.config.js +```less +// app.css -module.exports = { - plugins: [ - require('tailwindcss')('./tailwind.js'), - require('autoprefixer'), - ] -} +@tailwind preflight; + +@tailwind components; + +@tailwind utilities; ``` --- -```less -// main.less +```js +// webpack.mix.js -@tailwind preflight; +let mix = require('laravel-mix'); -// Any custom styles. +require('laravel-mix-tailwind'); -@tailwind utilities; +mix.postCss('build', 'assets/css/app.css') + .tailwind(); ``` --- @@ -152,15 +212,10 @@ module.exports = { // webpack.config.js Encore - ... - .enableLessLoader() - .addStyleEntry('site', './less/main.less') - .enablePostCssLoader(function(options) { - options.config = { - path: 'postcss.config.js' - }; - }) - ... + .setOutputPath('public/build/') + .setPublicPath('/build') + .addStyleEntry('app', './assets/css/app.css') + .enablePostCssLoader() ; ``` @@ -168,16 +223,28 @@ Encore --- +```js +// postcss.config.js + +module.exports = { + plugins: [ + require('tailwindcss')('./tailwind.js'), + ] +} +``` + +--- + ```html Hello, World! - + ... ``` --- -``` +```css .text-left { text-align: left; } @@ -193,19 +260,27 @@ Encore .text-justify { text-align: justify; } + +... ``` --- ```html -
- Hello, world! +
+

Hello, PHP South Wales!

``` --- -## __Configuration__ +![](images/1.png) + +--- + +[.header: alignment(center)] + +# Configuration --- @@ -329,9 +404,10 @@ options: { --- -## __States__ +[.header: alignment(center)] -### Hover, Focus, Active +# States +### _Hover, Focus, Active, Group hover_ --- @@ -347,9 +423,30 @@ options: { ``` +^ Hover, focus + --- -## __Responsive__ +```twig + + + +
+ {{ speaker.name }} +
+
+``` + +^ Group hover + +--- + +[.header: alignment(center)] + +# Responsive --- @@ -360,14 +457,23 @@ options: { --- ```html -
+

Hello, World!

``` --- -## __Extracting
Components__ +[.header: alignment(center)] + +# Extracting
Components + +--- + +[.header: alignment(center)] + + +## _Do you need to_ extract
a component? --- @@ -379,6 +485,29 @@ options: { {% endfor %} ``` +^ Use a loop + +--- + +```twig +

Adults

+ +{% include 'class-list' with { + classes: page.classes, + type: 'adults', +} %} + +

Kids

+ +{% include 'class-list' with { + classes: page.classes, + type: 'kids', +} %} +``` + +^ Move the duplicate markup into a partial, so there's only one version +Pass data in. + --- ```html @@ -393,21 +522,6 @@ options: { --- -```less -// main.less - -.button { - .inline-block; - .rounded; - .text-sm; - .py-2; - .px-3; - .text-white; -} -``` - ---- - ```css # main.css @@ -467,13 +581,17 @@ options: { --- -## Advantages +[.header: #53B0EB] + +# Advantages - Quick to prototype and make changes - Write less CSS - More consistency - Easy to customise, promote to components - Mix and match with normal CSS +- Easy to write reusable plugins +- Use PurgeCSS to remove unused classes ^ Do more with browser dev tools Only picking from pre-defined colours, widths. No magic numbers. @@ -482,7 +600,9 @@ Same classes, but no visual similarities like with other frameworks like Bootstr --- -## Disadvantages +[.header: #53B0EB] + +# Disadvantages - Extra build tools and steps - Lots of classes in markup @@ -490,19 +610,29 @@ Same classes, but no visual similarities like with other frameworks like Bootstr ^ - Need a build tool (Gulp, Grunt, Webpack) to build CSS +--- + +![inline](images/refactoring-ui.png) --- -## For Drupal - -- Add a prefix to avoid clashing -- Enable `important` setting to override existing styles -- Add classes in templates where possible -- Use `@apply` to limit the number of templates, to avoid adding classes in PHP code or config (e.g. Views), to style hard-to-reach elements +![inline](images/laravel-nova.png) --- -## Resources + +![inline](images/spatie.png) + +--- + +![inline](images/php-south-wales.png) + +--- + +[.build-lists: false] +[.header: #53B0EB] + +# Resources - tailwindcss.com - tailwindcomponents.com @@ -513,6 +643,9 @@ Same classes, but no visual similarities like with other frameworks like Bootstr --- -## __Thank you__ +[.header: alignment(center)] + +# Thanks! +### _@opdavies_ +### _oliverdavies.uk_ -### @opdavies diff --git a/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides.pdf b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides.pdf new file mode 100644 index 0000000..72a71d9 Binary files /dev/null and b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides.pdf differ diff --git a/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/1.png b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/1.png new file mode 100644 index 0000000..27ac156 Binary files /dev/null and b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/1.png differ diff --git a/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/10.png b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/10.png new file mode 100644 index 0000000..c1f0364 Binary files /dev/null and b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/10.png differ diff --git a/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/11.png b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/11.png new file mode 100644 index 0000000..b941238 Binary files /dev/null and b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/11.png differ diff --git a/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/12.png b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/12.png new file mode 100644 index 0000000..a91d7c8 Binary files /dev/null and b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/12.png differ diff --git a/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/13.png b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/13.png new file mode 100644 index 0000000..a9c33d4 Binary files /dev/null and b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/13.png differ diff --git a/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/14.png b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/14.png new file mode 100644 index 0000000..768084c Binary files /dev/null and b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/14.png differ diff --git a/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/15.png b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/15.png new file mode 100644 index 0000000..bac10ec Binary files /dev/null and b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/15.png differ diff --git a/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/16.png b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/16.png new file mode 100644 index 0000000..982d550 Binary files /dev/null and b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/16.png differ diff --git a/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/17.png b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/17.png new file mode 100644 index 0000000..06459bc Binary files /dev/null and b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/17.png differ diff --git a/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/18.png b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/18.png new file mode 100644 index 0000000..bf14ffd Binary files /dev/null and b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/18.png differ diff --git a/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/19.png b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/19.png new file mode 100644 index 0000000..52bb006 Binary files /dev/null and b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/19.png differ diff --git a/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/2.png b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/2.png new file mode 100644 index 0000000..8f85564 Binary files /dev/null and b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/2.png differ diff --git a/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/20.png b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/20.png new file mode 100644 index 0000000..94737da Binary files /dev/null and b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/20.png differ diff --git a/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/21.png b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/21.png new file mode 100644 index 0000000..4dcf719 Binary files /dev/null and b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/21.png differ diff --git a/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/22.png b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/22.png new file mode 100644 index 0000000..c1f0364 Binary files /dev/null and b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/22.png differ diff --git a/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/23.png b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/23.png new file mode 100644 index 0000000..c59007f Binary files /dev/null and b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/23.png differ diff --git a/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/24.png b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/24.png new file mode 100644 index 0000000..a0aa06c Binary files /dev/null and b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/24.png differ diff --git a/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/25.png b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/25.png new file mode 100644 index 0000000..e8c2d77 Binary files /dev/null and b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/25.png differ diff --git a/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/26.png b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/26.png new file mode 100644 index 0000000..c31712e Binary files /dev/null and b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/26.png differ diff --git a/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/27.png b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/27.png new file mode 100644 index 0000000..50ee2c7 Binary files /dev/null and b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/27.png differ diff --git a/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/28.png b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/28.png new file mode 100644 index 0000000..27103c6 Binary files /dev/null and b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/28.png differ diff --git a/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/29.png b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/29.png new file mode 100644 index 0000000..59d954c Binary files /dev/null and b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/29.png differ diff --git a/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/3.png b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/3.png new file mode 100644 index 0000000..ecc669c Binary files /dev/null and b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/3.png differ diff --git a/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/30.png b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/30.png new file mode 100644 index 0000000..6fdf3a0 Binary files /dev/null and b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/30.png differ diff --git a/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/31.png b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/31.png new file mode 100644 index 0000000..e651792 Binary files /dev/null and b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/31.png differ diff --git a/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/32.png b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/32.png new file mode 100644 index 0000000..4c15a6f Binary files /dev/null and b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/32.png differ diff --git a/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/33.png b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/33.png new file mode 100644 index 0000000..760b2e2 Binary files /dev/null and b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/33.png differ diff --git a/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/34.png b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/34.png new file mode 100644 index 0000000..7565860 Binary files /dev/null and b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/34.png differ diff --git a/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/35.png b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/35.png new file mode 100644 index 0000000..7be12f6 Binary files /dev/null and b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/35.png differ diff --git a/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/36.png b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/36.png new file mode 100644 index 0000000..b63b523 Binary files /dev/null and b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/36.png differ diff --git a/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/37.png b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/37.png new file mode 100644 index 0000000..a71070c Binary files /dev/null and b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/37.png differ diff --git a/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/38.png b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/38.png new file mode 100644 index 0000000..1f6ccc0 Binary files /dev/null and b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/38.png differ diff --git a/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/39.png b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/39.png new file mode 100644 index 0000000..693ebec Binary files /dev/null and b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/39.png differ diff --git a/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/4.png b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/4.png new file mode 100644 index 0000000..52d6197 Binary files /dev/null and b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/4.png differ diff --git a/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/40.png b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/40.png new file mode 100644 index 0000000..f4674f2 Binary files /dev/null and b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/40.png differ diff --git a/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/41.png b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/41.png new file mode 100644 index 0000000..46f1b58 Binary files /dev/null and b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/41.png differ diff --git a/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/42.png b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/42.png new file mode 100644 index 0000000..8377f42 Binary files /dev/null and b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/42.png differ diff --git a/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/43.png b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/43.png new file mode 100644 index 0000000..c20bacb Binary files /dev/null and b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/43.png differ diff --git a/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/44.png b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/44.png new file mode 100644 index 0000000..431f2e2 Binary files /dev/null and b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/44.png differ diff --git a/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/45.png b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/45.png new file mode 100644 index 0000000..66b8728 Binary files /dev/null and b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/45.png differ diff --git a/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/46.png b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/46.png new file mode 100644 index 0000000..c3a1c33 Binary files /dev/null and b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/46.png differ diff --git a/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/47.png b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/47.png new file mode 100644 index 0000000..9792224 Binary files /dev/null and b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/47.png differ diff --git a/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/48.png b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/48.png new file mode 100644 index 0000000..eb26ef0 Binary files /dev/null and b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/48.png differ diff --git a/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/49.png b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/49.png new file mode 100644 index 0000000..c4b19da Binary files /dev/null and b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/49.png differ diff --git a/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/5.png b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/5.png new file mode 100644 index 0000000..612b630 Binary files /dev/null and b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/5.png differ diff --git a/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/50.png b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/50.png new file mode 100644 index 0000000..1052a89 Binary files /dev/null and b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/50.png differ diff --git a/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/51.png b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/51.png new file mode 100644 index 0000000..f35568a Binary files /dev/null and b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/51.png differ diff --git a/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/52.png b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/52.png new file mode 100644 index 0000000..201d344 Binary files /dev/null and b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/52.png differ diff --git a/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/53.png b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/53.png new file mode 100644 index 0000000..f362d4c Binary files /dev/null and b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/53.png differ diff --git a/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/54.png b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/54.png new file mode 100644 index 0000000..f71d5ac Binary files /dev/null and b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/54.png differ diff --git a/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/6.png b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/6.png new file mode 100644 index 0000000..57fa577 Binary files /dev/null and b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/6.png differ diff --git a/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/7.png b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/7.png new file mode 100644 index 0000000..8c30ba6 Binary files /dev/null and b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/7.png differ diff --git a/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/8.png b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/8.png new file mode 100644 index 0000000..781840a Binary files /dev/null and b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/8.png differ diff --git a/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/9.png b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/9.png new file mode 100644 index 0000000..c0ef4bb Binary files /dev/null and b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides/9.png differ diff --git a/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides3.pdf b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides3.pdf new file mode 100644 index 0000000..474d827 Binary files /dev/null and b/taking-flight-with-tailwind-css/2018-07-31-php-south-wales/slides3.pdf differ diff --git a/taking-flight-with-tailwind-css/abstract.md b/taking-flight-with-tailwind-css/abstract.md deleted file mode 100644 index d0b6df6..0000000 --- a/taking-flight-with-tailwind-css/abstract.md +++ /dev/null @@ -1,10 +0,0 @@ -# Taking Flight with Tailwind CSS - -An introduction to utility class and component based styling, and how to soar with Tailwind CSS. - -Things we’ll cover: - -- Advantages and disadvantages to utility based styling and Tailwind. -- How to install Tailwind and add it to your build process. -- How to customise Tailwind. -- How to promote repeating classes into re-usable components.- diff --git a/tdd-test-driven-drupal/2018-06-14-drupal-somerset/slides2.pdf b/tdd-test-driven-drupal/2018-06-14-drupal-somerset/slides2.pdf new file mode 100644 index 0000000..c6c4eaf Binary files /dev/null and b/tdd-test-driven-drupal/2018-06-14-drupal-somerset/slides2.pdf differ diff --git a/tdd-test-driven-drupal/2018-07-05-drupal-dev-days/slides.pdf b/tdd-test-driven-drupal/2018-07-05-drupal-dev-days/slides.pdf new file mode 100644 index 0000000..70a1135 Binary files /dev/null and b/tdd-test-driven-drupal/2018-07-05-drupal-dev-days/slides.pdf differ diff --git a/tdd-test-driven-drupal/2018-07-05-drupal-dev-days/tdd-test-driven-drupal.pdf b/tdd-test-driven-drupal/2018-07-05-drupal-dev-days/tdd-test-driven-drupal.pdf new file mode 100644 index 0000000..77964ee Binary files /dev/null and b/tdd-test-driven-drupal/2018-07-05-drupal-dev-days/tdd-test-driven-drupal.pdf differ diff --git a/tdd-test-driven-drupal/abstract.md b/tdd-test-driven-drupal/abstract.md deleted file mode 100644 index 5eb4afc..0000000 --- a/tdd-test-driven-drupal/abstract.md +++ /dev/null @@ -1,11 +0,0 @@ -# TDD - Test Driven Drupal - -Testing is important. Why? It allows developers to add new features and edit and refactor existing code without the worry of adding regressions, reduces the reliance on manual testing to discover bugs, and by taking a test driven approach, your implementation code is leaner as you only write what is needed for your tests to pass. - -Drupal 7 includes the SimpleTest module for unit and functional testing, whilst Drupal 8 also includes and supports PHPUnit - the defacto PHP testing framework, used by other PHP projects including Symfony and Laravel - making it easier for people to test their code. Also, with testing being one of the Drupal core gates with tests needing to be included with every new feature or bug fix, and core’s 100% pass rate policy, testing has become an essential skill when contributing to core, or when working on your own projects. - -In this talk, we’ll cover the methodology and terminology involved with automated testing, and then take a test driven approach to creating a new Drupal module. - -**Note:** - -Currently updating this talk with more Drupal 8 content and examples. diff --git a/tdd-test-driven-drupal/images/broadbean-drupal-flow-1.png b/tdd-test-driven-drupal/images/broadbean-drupal-flow-1.png new file mode 100644 index 0000000..860c6c3 Binary files /dev/null and b/tdd-test-driven-drupal/images/broadbean-drupal-flow-1.png differ diff --git a/tdd-test-driven-drupal/images/broadbean-drupal-flow-2.png b/tdd-test-driven-drupal/images/broadbean-drupal-flow-2.png new file mode 100644 index 0000000..da80f78 Binary files /dev/null and b/tdd-test-driven-drupal/images/broadbean-drupal-flow-2.png differ diff --git a/tdd-test-driven-drupal/images/d8-simpletest-1.png b/tdd-test-driven-drupal/images/d8-simpletest-1.png new file mode 100644 index 0000000..5ef734a Binary files /dev/null and b/tdd-test-driven-drupal/images/d8-simpletest-1.png differ diff --git a/tdd-test-driven-drupal/images/d8-simpletest-2.png b/tdd-test-driven-drupal/images/d8-simpletest-2.png new file mode 100644 index 0000000..6a86356 Binary files /dev/null and b/tdd-test-driven-drupal/images/d8-simpletest-2.png differ diff --git a/tdd-test-driven-drupal/images/d8-simpletest-3.png b/tdd-test-driven-drupal/images/d8-simpletest-3.png new file mode 100644 index 0000000..9f2e423 Binary files /dev/null and b/tdd-test-driven-drupal/images/d8-simpletest-3.png differ diff --git a/tdd-test-driven-drupal/images/d8-simpletest-4.png b/tdd-test-driven-drupal/images/d8-simpletest-4.png new file mode 100644 index 0000000..162c867 Binary files /dev/null and b/tdd-test-driven-drupal/images/d8-simpletest-4.png differ diff --git a/tdd-test-driven-drupal/images/d8-simpletest-5.png b/tdd-test-driven-drupal/images/d8-simpletest-5.png new file mode 100644 index 0000000..2e1e4fe Binary files /dev/null and b/tdd-test-driven-drupal/images/d8-simpletest-5.png differ diff --git a/tdd-test-driven-drupal/images/d8-simpletest-6.png b/tdd-test-driven-drupal/images/d8-simpletest-6.png new file mode 100644 index 0000000..33e29f6 Binary files /dev/null and b/tdd-test-driven-drupal/images/d8-simpletest-6.png differ diff --git a/tdd-test-driven-drupal/images/d8-simpletest-7.png b/tdd-test-driven-drupal/images/d8-simpletest-7.png new file mode 100644 index 0000000..9b5463d Binary files /dev/null and b/tdd-test-driven-drupal/images/d8-simpletest-7.png differ diff --git a/tdd-test-driven-drupal/images/ddd-1.jpeg b/tdd-test-driven-drupal/images/ddd-1.jpeg new file mode 100644 index 0000000..6f49fc8 Binary files /dev/null and b/tdd-test-driven-drupal/images/ddd-1.jpeg differ diff --git a/tdd-test-driven-drupal/images/ddd-2.jpeg b/tdd-test-driven-drupal/images/ddd-2.jpeg new file mode 100644 index 0000000..9f5261d Binary files /dev/null and b/tdd-test-driven-drupal/images/ddd-2.jpeg differ diff --git a/tdd-test-driven-drupal/images/ddd-3.jpeg b/tdd-test-driven-drupal/images/ddd-3.jpeg new file mode 100644 index 0000000..a09ca7d Binary files /dev/null and b/tdd-test-driven-drupal/images/ddd-3.jpeg differ diff --git a/tdd-test-driven-drupal/images/ddd-4.jpeg b/tdd-test-driven-drupal/images/ddd-4.jpeg new file mode 100644 index 0000000..bbfce47 Binary files /dev/null and b/tdd-test-driven-drupal/images/ddd-4.jpeg differ diff --git a/tdd-test-driven-drupal/images/ddd-5.jpg b/tdd-test-driven-drupal/images/ddd-5.jpg new file mode 100644 index 0000000..7d575a3 Binary files /dev/null and b/tdd-test-driven-drupal/images/ddd-5.jpg differ diff --git a/tdd-test-driven-drupal/images/ddd-5.png b/tdd-test-driven-drupal/images/ddd-5.png new file mode 100644 index 0000000..a75093a Binary files /dev/null and b/tdd-test-driven-drupal/images/ddd-5.png differ diff --git a/tdd-test-driven-drupal/images/phpstorm-integration.png b/tdd-test-driven-drupal/images/phpstorm-integration.png new file mode 100644 index 0000000..c0d12c7 Binary files /dev/null and b/tdd-test-driven-drupal/images/phpstorm-integration.png differ diff --git a/tdd-test-driven-drupal/images/tada.png b/tdd-test-driven-drupal/images/tada.png new file mode 100644 index 0000000..4978ad7 Binary files /dev/null and b/tdd-test-driven-drupal/images/tada.png differ diff --git a/tdd-test-driven-drupal/images/tawny-tweet-1.png b/tdd-test-driven-drupal/images/tawny-tweet-1.png new file mode 100644 index 0000000..bf954fa Binary files /dev/null and b/tdd-test-driven-drupal/images/tawny-tweet-1.png differ diff --git a/tdd-test-driven-drupal/images/tawny-tweet-2.png b/tdd-test-driven-drupal/images/tawny-tweet-2.png new file mode 100644 index 0000000..ea425d4 Binary files /dev/null and b/tdd-test-driven-drupal/images/tawny-tweet-2.png differ diff --git a/tdd-test-driven-drupal/images/tdd-blog-1.png b/tdd-test-driven-drupal/images/tdd-blog-1.png new file mode 100644 index 0000000..ea4fdc0 Binary files /dev/null and b/tdd-test-driven-drupal/images/tdd-blog-1.png differ diff --git a/tdd-test-driven-drupal/images/tdd-blog-2.png b/tdd-test-driven-drupal/images/tdd-blog-2.png new file mode 100644 index 0000000..3bce812 Binary files /dev/null and b/tdd-test-driven-drupal/images/tdd-blog-2.png differ diff --git a/tdd-test-driven-drupal/images/tdd-blog-3.png b/tdd-test-driven-drupal/images/tdd-blog-3.png new file mode 100644 index 0000000..3b20e02 Binary files /dev/null and b/tdd-test-driven-drupal/images/tdd-blog-3.png differ diff --git a/tdd-test-driven-drupal/images/tdd-blog-4.png b/tdd-test-driven-drupal/images/tdd-blog-4.png new file mode 100644 index 0000000..66a0b80 Binary files /dev/null and b/tdd-test-driven-drupal/images/tdd-blog-4.png differ diff --git a/tdd-test-driven-drupal/images/tdd-blog-5.png b/tdd-test-driven-drupal/images/tdd-blog-5.png new file mode 100644 index 0000000..b50dd8a Binary files /dev/null and b/tdd-test-driven-drupal/images/tdd-blog-5.png differ diff --git a/tdd-test-driven-drupal/images/tdd-blog-directories.png b/tdd-test-driven-drupal/images/tdd-blog-directories.png new file mode 100644 index 0000000..17eb9fa Binary files /dev/null and b/tdd-test-driven-drupal/images/tdd-blog-directories.png differ diff --git a/tdd-test-driven-drupal/images/tdd-blog-installed.png b/tdd-test-driven-drupal/images/tdd-blog-installed.png new file mode 100644 index 0000000..e40926b Binary files /dev/null and b/tdd-test-driven-drupal/images/tdd-blog-installed.png differ diff --git a/tdd-test-driven-drupal/slides.md b/tdd-test-driven-drupal/slides.md index ff1be34..b0cba79 100644 --- a/tdd-test-driven-drupal/slides.md +++ b/tdd-test-driven-drupal/slides.md @@ -1,12 +1,11 @@ +theme: poster, 8 autoscale: true build-lists: true -footer-style: alignment(left) -footer: @opdavies | oliverdavies.uk header-emphasis: #53B0EB header: alignment(left) text: alignment(left) text-emphasis: #53B0EB -theme: poster, 8 +code: Monaco, #6699FF, #999999, #6666FF, #66FF66, #66FF66, line-height(1.3) [.header: alignment(center)] @@ -16,19 +15,77 @@ theme: poster, 8 --- +[.header: alignment(center)] + +## [fit] opdavi.es/_tdd-test-driven-drupal_ + +^ View on the website or click through to Speakerdeck + +--- + +[.background-color: #FFFFFF] +[.header: #111111, alignment(left)] + +## Diamond sponsor + +![inline 100%](images/ddd-2.jpeg) + +--- + +[.background-color: #FFFFFF] +[.header: #111111, alignment(left)] + +## Platinum sponsors + +![inline 100%](images/ddd-3.jpeg) + +--- + +[.background-color: #FFFFFF] +[.header: #111111, alignment(left)] + +## Gold sponsors + +![inline 100%](images/ddd-4.jpeg) + +--- + +![fit](images/ddd-5.jpg) + +--- + +[.build-lists: false] + +- Module and theme developers +- Want to know more about automated testing +- Looking to start writing your first tests +- Drupal 8 +- PHPUnit + +--- + +- Why write tests, and what to test +- Types of tests +- How to run tests +- Real life example +- Building a new module with TDD + +--- + [.background-color: #FFFFFF] [.build-lists: false] [.header: #111111] [.text: #111111, alignment(left)] -![right 1000%](../me-phpnw.png) +![right 800%](../images/me-phpnw.png) -- Full stack Web Developer & System Administrator +- Full Stack Web Developer & System Administrator - Senior Developer at Microserve - Part-time freelancer - Acquia certified Drupal 8 Grand Master - Drupal 7 & 8 core contributor -- opdavies (Drupal.org, GitHub, Twitter) +- Symfony, Laravel, ~~Silex,~~ Sculpin +- @opdavies - www.oliverdavies.uk ^ Work at Microserve. @@ -37,6 +94,30 @@ Blog on my website --- +[.build-lists: false] + +- oliverdavies.uk/_talks_ +- oliverdavies.uk/_twitter_ +- oliverdavies.uk/_drupal_ +- oliverdavies.uk/_github_ + +^ Example code on GitHub + +--- + +[.background-color: #FFFFFF] +[.text: #111111, alignment(left)] + +![right 100%](../images/microserve-light.png) + +- https://microserve.io +- https://www.drupal.org/microserve +- https://github.com/microserve-io +- https://twitter.com/microserveltd +- https://www.linkedin.com/company/microserve-ltd + +--- + [.header: alignment(center)] ## test_driven_drupal_.com_ @@ -45,21 +126,19 @@ Blog on my website [.header: alignment(center)] -## Why? -## _What?_ -## How? +## Write custom modules and themes _for clients_ --- [.header: alignment(center)] -## I write _contrib modules_ for the community +## Occassionally
contribute _to core_ --- [.header: alignment(center)] -## I write _custom modules_ for client projects +## Maintain and contribute to _contrib projects_ --- @@ -73,13 +152,13 @@ Blog on my website - Become maintainer in 2012 - Had some existing tests -- First experience of testing with a real module - Used on _11,046 sites_ in October 2012 (_84_ D5, _7,094_ D6, _3,868_ D7) - Used on _29,023 sites_ in June 2018 (_9_ D5, _1,853_ D6, _23,602_ D7, _3,559_ D8) +- _#236_ most used module on Drupal.org - Crucial to preventing regressions when adding new features or fixing bugs -- Ensured consistency when porting to Drupal 8 ^ Preventing regressions in my additions but also user submitted patches +First module I ported to Drupal 8, aided by tests --- @@ -92,7 +171,7 @@ Blog on my website ## _Why write tests?_ - Catch bugs earlier -- Piece of mind +- Peace of mind - Prevent regressions - Write less code - Documentation @@ -107,56 +186,33 @@ ONO merge conflict ## _Core Testing Gate_ -New features should be accompanied by automated tests. - -If the feature does not have an implementation, provide a test implementation. - -Bug fixes should be accompanied by changes to a test (either modifying an existing test case or adding a new one) that demonstrate the bug. +- New features should be accompanied by automated tests. +- If the feature does not have an implementation, provide a test implementation. +- Bug fixes should be accompanied by changes to a test (either modifying an existing test case or adding a new one) that demonstrate the bug. [.footer: https://www.drupal.org/core/gates#testing] --- -[.header: alignment(center)] - -## _Testing may add time now,_ but save more
time in the future - ---- - -[.header: alignment(center)] - -## [fit] _How do you get quicker at writing tests?_ -# [fit] By writing more tests - ---- - ## _Testing in Drupal_ -- _Drupal 7_ - Simpletest (testing) module provided as part of core +- _Drupal 7_ - SimpleTest (testing) module provided as part of core - _Drupal 8_ - PHPUnit added as a core dependency -- _PHPUnit Initiative_ - Simpletest to be deprecated and removed in Drupal 9 +- _PHPUnit Initiative_ - SimpleTest to be deprecated and removed in Drupal 9 --- -## _Setting up your environment_ +[.header: #53B0EB] -- Drupal includes `core/phpunit.xml.dist` -- Copy to `core/phpunit.xml` -- Amend values as needed - + Add base URL, database credentials -- Docksal - `fin addon install phpunit` +## Writing Tests (Drupal 8) ---- - -## _Writing Tests (Drupal 8)_ - -- PHP class with `.php` extension -- `tests/src` directory within each module -- Within the `Drupal\Tests\module_name` namespace +- PHP class with _.php_ extension +- _tests/src_ directory within each module +- Within the *Drupal\Tests\module_name* namespace - Class name must match the filename - Namespace must match the directory structure - One test class per feature -- Each method must start with `test` +- Each method must start with _test_ ^ Different to D7 @@ -170,11 +226,8 @@ Bug fixes should be accompanied by changes to a test (either modifying an existi --- -[.hide-footer] ```php - 'data']); - public static $modules = ['phpunit_example']; - - public function testPhpUnitExampleMenu() { - $this->drupalGet('/examples/phpunit-example'); - - $this->assertSession()->statusCodeEquals(200); + $this->assertEquals('test', $job->getType()); + $this->assertEquals(['my' => 'data'], $job->getPayload()); + $this->assertEquals(Job::STATE_QUEUED, $job->getState()); } + } ``` ---- - -[.hide-footer] - -```php, [.highlight: 5] -drupalGet('/examples/phpunit-example'); - - $this->assertSession()->statusCodeEquals(200); - } -} -``` +^ Within a Unit directory and namespace +Called JobTest because it's testing the Job class +Called testCreate because it's testing the create method +Create a job with the create method +Retrieve data from the object with getters --- -[.hide-footer] - -```php, [.highlight: 7-9] -drupalGet('/examples/phpunit-example'); - - $this->assertSession()->statusCodeEquals(200); - } -} -``` - ---- - -[.hide-footer] - ```php, [.highlight: 11] - 'data']); - public static $modules = ['phpunit_example']; - - public function testPhpUnitExampleMenu() { - $this->drupalGet('/examples/phpunit-example'); - - $this->assertSession()->statusCodeEquals(200); + $this->assertEquals('test', $job->getType()); + $this->assertEquals(['my' => 'data'], $job->getPayload()); + $this->assertEquals(Job::STATE_QUEUED, $job->getState()); } + } ``` --- -[.hide-footer] +```php, [.highlight: 13-15] +// tests/src/Unit/JobTest.php -```php, [.highlight: 13-17] - 'data']); -class PHPUnitExampleMenuTest extends BrowserTestBase { - - public static $modules = ['phpunit_example']; - - public function testPhpUnitExampleMenu() { - $this->drupalGet('/examples/phpunit-example'); - - $this->assertSession()->statusCodeEquals(200); + $this->assertEquals('test', $job->getType()); + $this->assertEquals(['my' => 'data'], $job->getPayload()); + $this->assertEquals(Job::STATE_QUEUED, $job->getState()); } + } ``` --- +[.header: #53B0EB] -## _Kernel Tests_ +## Kernel Tests - Integration tests - Can install modules, interact with services, container, database @@ -366,80 +415,315 @@ class PHPUnitExampleMenuTest extends BrowserTestBase { --- -## _Unit Tests_ - -- Tests PHP logic -- No database interaction -- Fast to run -- Tightly coupled -- Complicated mocking - ---- - -[.hide-footer] - ```php -namespace Drupal\collection_class; +// tests/src/Kernel/ProcessorTest.php -class Collection implements \Countable, \IteratorAggregate { - private $items; +namespace Drupal\Tests\advancedqueue\Kernel; - public function __construct($items = array()) { - $this->items = is_array($items) ? $items - : $this->getArrayableItems($items); - } +use Drupal\advancedqueue\Entity\Queue; +use Drupal\advancedqueue\Job; +use Drupal\KernelTests\KernelTestBase; - public function __toString() { - return $this->toJson(); - } +class ProcessorTest extends KernelTestBase { - public function all() { - return $this->items; - } + ... - public function count() { - return count($this->items); - } - - - public function isEmpty() { - return empty($this->items); - } - - public function first() { - return array_shift($this->items); - } } ``` --- -[.hide-footer] - ```php -$collection = new Collection([1, 2, 3, 4, 5]); +// tests/src/Kernel/ProcessorTest.php -// Returns all items. -$collection->all(); +protected function setUp() { + parent::setUp(); -// Counts the number of items. -$collection->count(); + $this->installSchema('advancedqueue', ['advancedqueue']); -// Returns the array keys. -$collection->keys(); + $this->queue = Queue::create([ + 'id' => 'test', + 'label' => 'Test queue', + 'backend' => 'database', + 'backend_configuration' => [ + 'lease_time' => 5, + ], + ]); + $this->queue->save(); + + $this->processor = $this->container->get('advancedqueue.processor'); +} ``` --- -[.hide-footer] +```php, [.highlight: 6] +// tests/src/Kernel/ProcessorTest.php + +protected function setUp() { + parent::setUp(); + + $this->installSchema('advancedqueue', ['advancedqueue']); + + $this->queue = Queue::create([ + 'id' => 'test', + 'label' => 'Test queue', + 'backend' => 'database', + 'backend_configuration' => [ + 'lease_time' => 5, + ], + ]); + $this->queue->save(); + + $this->processor = $this->container->get('advancedqueue.processor'); +} +``` + +--- + +```php, [.highlight: 8-16] +// tests/src/Kernel/ProcessorTest.php + +protected function setUp() { + parent::setUp(); + + $this->installSchema('advancedqueue', ['advancedqueue']); + + $this->queue = Queue::create([ + 'id' => 'test', + 'label' => 'Test queue', + 'backend' => 'database', + 'backend_configuration' => [ + 'lease_time' => 5, + ], + ]); + $this->queue->save(); + + $this->processor = $this->container->get('advancedqueue.processor'); +} +``` + +--- + +```php, [.highlight: 18] +// tests/src/Kernel/ProcessorTest.php + +protected function setUp() { + parent::setUp(); + + $this->installSchema('advancedqueue', ['advancedqueue']); + + $this->queue = Queue::create([ + 'id' => 'test', + 'label' => 'Test queue', + 'backend' => 'database', + 'backend_configuration' => [ + 'lease_time' => 5, + ], + ]); + $this->queue->save(); + + $this->processor = $this->container->get('advancedqueue.processor'); +} +``` + +--- ```php -public function testAll() { - $items = ['foo', 'bar', 'baz']; +// tests/src/Kernel/ProcessorTest.php - $collection = new Collection($items); +public function testProcessor() { + $first_job = Job::create('simple', [ + 'test' => '1', + ]); - $this->assertEqual($items, $collection->all()); + $second_job = Job::create('flexible', [ + 'expected_state' => Job::STATE_SUCCESS, + 'expected_message' => 'Done!', + ]); + + $third_job = Job::create( + 'flexible', ['expected_exception' => 'DB down!'], + ); + + $fourth_job = Job::create('flexible', [ + 'expected_state' => Job::STATE_FAILURE, + 'expected_message' => 'Failed!', + ]); + + ... +} +``` + +--- + +```php, [.highlight: 6-10] +// tests/src/Kernel/ProcessorTest.php + +public function testProcessor() { + ... + + $this->queue->enqueueJob($first_job); + $this->queue->enqueueJob($second_job); + $this->queue->enqueueJob($third_job); + $this->queue->enqueueJob($fourth_job); + + $num_processed = $this->processor->processQueue($this->queue); + + $this->assertEquals(4, $num_processed); +} +``` + +--- + +```php, [.highlight: 11-13] +// tests/src/Kernel/ProcessorTest.php + +public function testProcessor() { + ... + + $this->queue->enqueueJob($first_job); + $this->queue->enqueueJob($second_job); + $this->queue->enqueueJob($third_job); + $this->queue->enqueueJob($fourth_job); + + $num_processed = $this->processor->processQueue($this->queue); + + $this->assertEquals(4, $num_processed); +} +``` + +--- + +[.header: #53B0EB] + +## Functional Tests + +- Tests end-to-end functionality +- UI testing +- Interacts with database +- Full Drupal installation +- Slower to run +- With/without JavaScript + +^ testing profile + +--- + +```php +// tests/src/Functional/QueueTest.php + +namespace Drupal\Tests\advancedqueue\Functional; + +use Drupal\advancedqueue\Entity\Queue; +use Drupal\advancedqueue\Entity\QueueInterface; +use Drupal\Tests\BrowserTestBase; + +class QueueTest extends BrowserTestBase { + ... +} +``` + +--- + +```php, [.highlight: 6-8] +// tests/src/Functional/QueueTest.php + +protected function setUp() { + parent::setUp(); + + $this->placeBlock('local_tasks_block'); + $this->placeBlock('local_actions_block'); + $this->placeBlock('page_title_block'); + + $this->adminUser = $this->drupalCreateUser(['administer advancedqueue']); + $this->drupalLogin($this->adminUser); +} +``` + +--- + +```php, [.highlight: 10-11] +// tests/src/Functional/QueueTest.php + +protected function setUp() { + parent::setUp(); + + $this->placeBlock('local_tasks_block'); + $this->placeBlock('local_actions_block'); + $this->placeBlock('page_title_block'); + + $this->adminUser = $this->drupalCreateUser(['administer advancedqueue']); + $this->drupalLogin($this->adminUser); +} +``` + +--- + +```php, [.highlight: 4-11] +// tests/src/Functional/QueueTest.php + +public function testQueueDeletion() { + $queue = Queue::create([ + 'id' => 'test', + 'label' => 'Test', + 'backend' => 'database', + 'processor' => QueueInterface::PROCESSOR_DAEMON, + 'processing_time' => 100, + ]); + $queue->save(); + $this->drupalGet('admin/config/system/queues/manage/' . $queue->id() . '/delete'); + $this->submitForm([], 'Delete'); + $this->assertSession()->addressEquals('admin/config/system/queues'); + + $queue_exists = (bool) Queue::load('test'); + $this->assertEmpty($queue_exists, 'The queue has been deleted from the database.'); +} +``` + +--- + +```php, [.highlight: 12-14] +// tests/src/Functional/QueueTest.php + +public function testQueueDeletion() { + $queue = Queue::create([ + 'id' => 'test', + 'label' => 'Test', + 'backend' => 'database', + 'processor' => QueueInterface::PROCESSOR_DAEMON, + 'processing_time' => 100, + ]); + $queue->save(); + $this->drupalGet('admin/config/system/queues/manage/' . $queue->id() . '/delete'); + $this->submitForm([], 'Delete'); + $this->assertSession()->addressEquals('admin/config/system/queues'); + + $queue_exists = (bool) Queue::load('test'); + $this->assertEmpty($queue_exists, 'The queue has been deleted from the database.'); +} +``` + +--- + +```php, [.highlight: 16-17] +// tests/src/Functional/QueueTest.php + +public function testQueueDeletion() { + $queue = Queue::create([ + 'id' => 'test', + 'label' => 'Test', + 'backend' => 'database', + 'processor' => QueueInterface::PROCESSOR_DAEMON, + 'processing_time' => 100, + ]); + $queue->save(); + $this->drupalGet('admin/config/system/queues/manage/' . $queue->id() . '/delete'); + $this->submitForm([], 'Delete'); + $this->assertSession()->addressEquals('admin/config/system/queues'); + + $queue_exists = (bool) Queue::load('test'); + $this->assertEmpty($queue_exists, 'The queue has been deleted from the database.'); } ``` @@ -467,9 +751,21 @@ public function testAll() { --- -## _Setup (functional)_ +## _Should you test that_
a block is rendered correctly? -[.hide-footer] +--- + +## _Or should you test_
your render array to generate the block? + +^ The answer might be 'both'. +The right type of test to use might not be that obvious. +You may be able to use a different type of test if you take a different approach. + +--- + +[.header: #53B0EB] + +## Setup (functional) ``` drupalCreateUser() @@ -504,15 +800,16 @@ assertMail() --- -## _Assertions_ +[.header: #53B0EB] -[.hide-footer] +## Assertions ```php assertTrue() assertFalse() assertEquals() +assertSame() assertNull() assertNotNull() @@ -525,11 +822,13 @@ assertArraySubset() --- -## _Assertions (functional)_ +[.header: #53B0EB] -[.hide-footer] +## Assertions (functional) ```php +assertSession() + pageTextContains() pageTextNotContains() @@ -555,17 +854,30 @@ statusCodeNotEquals() --- -## _Specification_ +[.header: #53B0EB] -- Job adverts created on third-party system, needs to create nodes in Drupal, links users to separate application system -- Adverts need to be linked to offices -- Advert length specified in number of days +## Specification + +- Job adverts created in Broadbean UI, needs to create nodes in Drupal +- Application URL links users to separate application system +- Jobs need to be linked to offices +- Job length specified in number of days - Path is specified as a field in the API - Application URL constructed from domain, includes role ID as a GET parameter and optionally UTM parameters --- -[.hide-footer] +[.background-color: #FFFFFF] + +![inline 125%](images/broadbean-drupal-flow-1.png) + +--- + +[.background-color: #FFFFFF] + +![inline 125%](images/broadbean-drupal-flow-2.png) + +--- ```php $data = [ @@ -594,17 +906,33 @@ $data = [ --- -## _Implementation_ +[.header: #53B0EB] + +## Implementation - Added route to accept data from API as XML -- Added user with API role to authenticate -- `active_for` converted from number of days to UNIX timestamp -- `branch_name` and `locations` converted from plain text to entity reference (job node to office node) -- `url_alias` property mapped to `path` +- Added system user with API role to authenticate +- *active_for* converted from number of days to UNIX timestamp +- *branch_name* and *locations* converted from plain text to entity reference (job node to office node) +- *url_alias* property mapped to *path* --- -## _Goals_ +[.header: #53B0EB] + +## Implementation + +- If no error, create the job node, return OK response to Broadbean +- If an Exception is thrown, return an error code and message + +^ Required field missing +Incorrect branch name + +--- + +[.header: #53B0EB] + +## Testing Goals - Ensure job nodes are _successfully created_ - Ensure that fields are _mapped correctly_ @@ -613,7 +941,9 @@ $data = [ --- -## _Types of tests_ +[.header: #53B0EB] + +## Types of tests - _Unit:_ ensure number of days are converted to timestamps correctly - _Kernel:_ job nodes can be added and deleted, expired job nodes are deleted, application URL is generated correctly @@ -622,7 +952,9 @@ $data = [ --- -## _Results_ +[.header: #53B0EB] + +## Results - _0 bugs!_ - Reduced debugging time @@ -636,19 +968,41 @@ $data = [ --- -![fit](images/simpletest-1.png) +### _Option 1_ +## SimpleTest module (UI) --- -![fit](images/simpletest-2.png) +![fit](images/d8-simpletest-1.png) --- -![fit](images/simpletest-3.png) +![fit](images/d8-simpletest-2.png) --- -![fit](images/simpletest-4.png) +![fit](images/d8-simpletest-3.png) + +--- + +![fit](images/d8-simpletest-4.png) + +--- + +![fit](images/d8-simpletest-5.png) + +--- + +![fit](images/d8-simpletest-6.png) + +--- + +![fit](images/d8-simpletest-7.png) + +--- + +### _Option 2_ +## Core script --- @@ -662,13 +1016,58 @@ $ php core/scripts/run-tests.sh --class ExampleTest --- -``` -vendor/bin/phpunit -c core path/to/module +### _Option 3_ +## PHPUnit -vendor/bin/phpunit -c core path/to/module --filter testSomething +--- + +## Prerequisite _(creating a phpunit.xml file)_ + +- Configures PHPUnit +- Needed to run some types of tests +- Ignored by Git by default +- Copy _core/phpunit.xml.dist_ to _core/phpunit.xml_ +- Add and change as needed + - `SIMPLETEST_BASE_URL`, `SIMPLETEST_DB`, `BROWSERTEST_OUTPUT_DIRECTORY` + - `stopOnFailure="true"` + +--- -vendor/bin/phpunit -c core path/to/module --verbose ``` +cd web + +../vendor/bin/phpunit -c core \ +modules/contrib/examples/phpunit_example +``` + +--- + +``` +cd web/core + +../../vendor/bin/phpunit \ +../modules/contrib/examples/phpunit_example +``` + +--- + +``` +--filter + +--testsuite + +--group + +--colors + +--stop-on-failure + +--verbose --debug +``` + +--- + +![fit](images/phpstorm-integration.png) --- @@ -678,12 +1077,15 @@ vendor/bin/phpunit -c core path/to/module --verbose --- -## _Test Driven Development_ +[.header: #53B0EB] + +## Test Driven Development - Write a test -- See it fail -- Write code until test passes -- Refactor when tests are green +- Test fails +- Write code +- Test passes +- Refactor - Repeat --- @@ -702,7 +1104,9 @@ vendor/bin/phpunit -c core path/to/module --verbose --- -## _Porting Modules to Drupal 8_ +[.header: #53B0EB] + +## Porting Modules to Drupal 8 - Make a new branch - Add/update the tests @@ -712,23 +1116,28 @@ vendor/bin/phpunit -c core path/to/module --verbose --- -## _How I Write Tests - "Outside In"_ +[.header: #53B0EB] + +## How I Write Tests - "Outside In" - Start with functional tests - Drop down to integration or unit tests where needed - Programming by wishful thinking - Write comments first, then fill in the code -- Write assertions first, sometimes +- Sometimes write assertions first --- [.header: alignment(center)] -## Building a new _Drupal 8 Module_ with _TDD_ +## [fit] _Building a new Drupal 8 Module with_ +## [fit] test driven development --- -## _Acceptance criteria_ +[.header: #53B0EB] + +## Acceptance criteria - As a site visitor - I want to see a list of published articles at /blog @@ -736,7 +1145,9 @@ vendor/bin/phpunit -c core path/to/module --verbose --- -## _Tasks_ +[.header: #53B0EB] + +## Tasks - Ensure the blog page exists - Ensure only published articles are shown @@ -744,7 +1155,9 @@ vendor/bin/phpunit -c core path/to/module --verbose --- -## _Implementation_ +[.header: #53B0EB] + +## Implementation - Use views module - Do the mininum amount at each step, make no assumptions, let the tests guide us @@ -752,28 +1165,27 @@ vendor/bin/phpunit -c core path/to/module --verbose --- -[.hide-footer] +### _Step 1_ +## Create the module + +--- ```yml # tdd_blog.info.yml -name: 'TDD Example' +name: 'TDD Blog' core: '8.x' type: 'module' ``` --- -### _Task 1:_ +### _Step 2_ ## Ensure the blog page exists --- -[.hide-footer] - ```php -- _There are no filters on the view_ -- Add the filters -- Export and save the view +![inline](images/tdd-blog-3.png) + +--- + +>- _There is no content type filter on the view_ +- Add the filter +- Re-export and save the view + +--- + +![inline](images/tdd-blog-4.png) --- @@ -1562,6 +1970,8 @@ OK (1 test, 6 assertions) --- +[.build-lists: false] + ## _Tasks_ - ~~Ensure the blog page exists~~ @@ -1570,12 +1980,11 @@ OK (1 test, 6 assertions) --- -### _Task 3:_ -## Ensure the articles are shown in the correct order +### _Step 4_ +## Ensure the articles are ordered by date --- -[.hide-footer] ```php // modules/custom/tdd_blog/tests/src/Kernel/BlogPageTest.php @@ -1591,17 +2000,16 @@ public function testArticlesAreOrderedByDate() { --- -[.hide-footer] ```php, [.highlight: 4-9] // modules/custom/tdd_blog/tests/src/Kernel/BlogPageTest.php public function testArticlesAreOrderedByDate() { // Given that I have numerous articles with different post dates. - $this->createNode(['type' => 'article', 'created' => (new \DateTime())->modify('+1 day')->getTimestamp()]); - $this->createNode(['type' => 'article', 'created' => (new \DateTime())->modify('+1 month')->getTimestamp()]); - $this->createNode(['type' => 'article', 'created' => (new \DateTime())->modify('+3 days')->getTimestamp()]); - $this->createNode(['type' => 'article', 'created' => (new \DateTime())->modify('+1 hour')->getTimestamp()]); + $this->createNode(['type' => 'article', 'created' => (new \DateTime('+1 day'))->getTimestamp()]); + $this->createNode(['type' => 'article', 'created' => (new \DateTime('+1 month'))->getTimestamp()]); + $this->createNode(['type' => 'article', 'created' => (new \DateTime('+3 days'))->getTimestamp()]); + $this->createNode(['type' => 'article', 'created' => (new \DateTime('+1 hour'))->getTimestamp()]); // When I go to the blog page. @@ -1611,7 +2019,17 @@ public function testArticlesAreOrderedByDate() { --- -[.hide-footer] +```php +$this->createNode([ + 'type' => 'article', + 'created' => (new \DateTime())->modify('+1 day')->getTimestamp(), +]); +``` + +^ Array of default values + +--- + ```php, [.highlight: 10-11] // modules/custom/tdd_blog/tests/src/Kernel/BlogPageTest.php @@ -1632,8 +2050,6 @@ public function testArticlesAreOrderedByDate() { --- -[.hide-footer] - ```php, [.highlight:10-15] // modules/custom/tdd_blog/tests/src/Kernel/BlogPageTest.php @@ -1657,7 +2073,6 @@ public function testArticlesAreOrderedByDate() { --- -[.hide-footer] ```php, [.highlight: 5-9, 17-18] // modules/custom/tdd_blog/tests/src/Kernel/BlogPageTest.php @@ -1683,7 +2098,6 @@ public function testArticlesAreOrderedByDate() { --- -[.hide-footer] ``` PHPUnit 6.5.8 by Sebastian Bergmann and contributors. @@ -1705,10 +2119,10 @@ Failed asserting that two arrays are equal. - 1 => 1 - 2 => 3 - 3 => 2 -+ 0 => '3' ++ 0 => '1' + 1 => '2' -+ 2 => '4' -+ 3 => '1' ++ 2 => '3' ++ 3 => '4' /Users/opdavies/Code/drupal-testing-workshop/web/core/tests/Drupal/KernelTests/KernelTestBase.php:1114 /Users/opdavies/Code/drupal-testing-workshop/web/modules/custom/tdd_blog/tests/src/Kernel/BlogPageTest.php:43 @@ -1719,7 +2133,7 @@ Tests: 1, Assertions: 4, Failures: 1. --- -[.hide-footer] +[.text: comic sans] ```[.highlight: 8-26] PHPUnit 6.5.8 by Sebastian Bergmann and contributors. @@ -1741,10 +2155,10 @@ Failed asserting that two arrays are equal. - 1 => 1 - 2 => 3 - 3 => 2 -+ 0 => '3' ++ 0 => '1' + 1 => '2' -+ 2 => '4' -+ 3 => '1' ++ 2 => '3' ++ 3 => '4' /Users/opdavies/Code/drupal-testing-workshop/web/core/tests/Drupal/KernelTests/KernelTestBase.php:1114 /Users/opdavies/Code/drupal-testing-workshop/web/modules/custom/tdd_blog/tests/src/Kernel/BlogPageTest.php:43 @@ -1755,12 +2169,20 @@ Tests: 1, Assertions: 4, Failures: 1. --- +![inline](images/tdd-blog-4.png) + +--- + - _There is no sort order defined on the view_ - Add the sort order - Re-export the view --- +![inline](images/tdd-blog-5.png) + +--- + ```[.highlight:3-8] PHPUnit 6.5.8 by Sebastian Bergmann and contributors. @@ -1774,6 +2196,8 @@ OK (1 test, 5 assertions) --- +[.build-lists: false] + ## _Tasks_ - ~~Ensure the blog page exists~~ @@ -1782,6 +2206,21 @@ OK (1 test, 5 assertions) --- +![inline fit](images/tdd-blog-directories.png) + +--- + +![fit](images/tdd-blog-installed.png) + +^ Using the minimal installation profile +Post 3 is unpublished + +--- + +![inline 75%](images/tada.png) + +--- + [.header: alignment(center)] ## Take Aways @@ -1806,40 +2245,88 @@ Manual testing is still important --- -[.hide-footer] +[.header: alignment(center)] + +## [fit] _Having tests does not mean_ +## [fit] there will be no bugs + +^ Only means that the tests you wrote are passing +You may not have included a certain use case +Be sure to test in the UI! +We can test what happens in a test when a user has a permission, but in our site we still need to assign the permission to a role and the role to a user. + +--- + +### _You might be testing the wrong thing_ +## Maybe it doesn't work the way you think it does + +--- + +### _Have you written enough assertions?_ +## Have you only covered the 'happy path' scenarios? + +^ If your tests are passing but there is an issue, maybe you haven't written enough assertions +Be sure to check for the negative use cases too +Check that something is not included as well as what should be included +What if you pass in an incorrect value? + +--- + +### _Other modules can affect things_ +## Tests may pass, but fail when other modules are enabled + +--- + +[.header: alignment(center)] + +## [fit] _Testing may add time now_ +## [fit] but save more time in the future + +--- + +[.header: alignment(center)] + +## [fit] _How do you get quicker at writing tests?_ +# [fit] By writing more tests + +^ Practice makes perfect +Become more familar with and knowledge of recurring errors +Find better practices and approaches. Different base classes? Less setup steps. Less time, more productive. + +--- + +## _Start small_ +## Some tests are better than no tests + +--- + + +[.background-color: #FFFFFF] + +![140%](images/tawny-tweet-1.png) + +--- + +[.background-color: #FFFFFF] + +![150%](images/tawny-tweet-2.png) + +--- + [.text: alignment(center)] > ![inline 150%](images/when-you-do-things-right.jpg) --- -## _Resources_ - -- github.com/opdavies/drupal-module-tdd-dublin -- drupalize.me/series/testing-drupal-7-simpletest -- lullabot.com/articles/an-overview-of-testing-in-drupal-8 -- mediacurrent.com/blog/writing-simple-simpletest-tests-your-d7-module -- mediacurrent.com/blog/writing-simple-phpunit-tests-your-d8-module -- knpuniversity.com/screencast/phpunit -- adamwathan.me/test-driven-laravel -- laracasts.com - ---- - -- oliverdavies.uk/_talks_ -- oliverdavies.uk/_twitter_ -- oliverdavies.uk/_drupal_ -- oliverdavies.uk/_github_ -- oliverdavies.uk/_youtube_ - ---- - [.header: alignment(center)] -## Questions? +# Questions? --- [.header: alignment(center)] # Thanks +### _@opdavies_ +### _oliverdavies.uk_ diff --git a/tdd-test-driven-drupal/todo.md b/tdd-test-driven-drupal/todo.md index 63b93e0..da818b0 100644 --- a/tdd-test-driven-drupal/todo.md +++ b/tdd-test-driven-drupal/todo.md @@ -1,2 +1,3 @@ - Show composer.json setup -- Show `fin phpunit` +- Add "things you can test" and "things you shouldn't test" + diff --git a/using-laravel-collections-outside-laravel/2017-12-21-nomad-php/slides.md b/using-laravel-collections-outside-laravel/2017-12-21-nomad-php/slides.md index c4935df..4b69727 100644 --- a/using-laravel-collections-outside-laravel/2017-12-21-nomad-php/slides.md +++ b/using-laravel-collections-outside-laravel/2017-12-21-nomad-php/slides.md @@ -1,25 +1,25 @@ +theme: poster, 8 autoscale: true build-lists: true -theme: simple, 1 +header-emphasis: #53B0EB +header: alignment(left) +text: alignment(left) +text-emphasis: #53B0EB +code: Operator Mono, line-height(1.5) -# __Using Laravel Collections...
Outside Laravel__ +[.background-color: #FFFFFF] +[.hide-footer] +[.header: #111111, alignment(center)] + +## Using Laravel Collections...
Outside Laravel ![250%](images/laravel.png) --- -[.build-lists: false] +[.header: alignment(center)] -- Web Developer -- Drupal, Symfony, Silex, Laravel, Sculpin -- @opdavies -- oliverdavies.uk - -![right](../../me-phpnw.png) - ---- - -## Collections++ +## Collections :thumbsup: ^ Became a fan of Collections whilst learning Laravel Powerful object orientated way to interact with arrays @@ -92,6 +92,8 @@ $collection->filter(function ($person) { --- +[.background-color: #FFFFFF] + ![100%](images/druplicon.png) ^ This is great, but how can I do that in my Drupal code? @@ -99,13 +101,15 @@ How can I do that? --- -> There’s a module for that! -> -- Drupalers +## There’s a module for that! +### _- Drupalers_ --- -> There's not a module for that. :( --- Me +[.text: alignment(center)] + +## [fit] There's not a module for that. :disappointed: +### _- Me_ --- @@ -116,7 +120,8 @@ How can I do that? --- -## 1.0: Write my own Collection class +### _Version 1.0_ +## Write my own Collection class ^ Wrote my own Collection class Wrote my own tests @@ -125,7 +130,7 @@ Wrote my own tests ![](images/drupal-issue-1.png) -^ Maybe 70% of what Laravels' could do. +^ Maybe 70% of what Laravel's could do. --- @@ -136,23 +141,25 @@ Wrote my own tests --- -> Collect - Illuminate Collections as a separate package. -> -- https://packagist.org/packages/tightenco/collect +### Collect - Illuminate Collections as a separate package. +#### _https://packagist.org/packages/tightenco/collect_ --- -> Import Laravel's Collections into non-Laravel packages easily, without needing to require the entire Illuminate\Support package. -> -- https://packagist.org/packages/tightenco/collect +### Import Laravel's Collections into non-Laravel packages easily, without needing to require the entire Illuminate\Support package. +#### _https://packagist.org/packages/tightenco/collect_ --- +[.background-color: #FFFFFF] + ![600%](images/composer.png) ^ Can install via Composer --- -## `composer require tightenco/collect` +## _composer require_
tightenco/collect --- ![](images/drupal-issue-2.png) @@ -163,13 +170,15 @@ Wrote my own tests --- -## ~~1.0: Write my own Collection class~~ -## 2.0: Use someone else’s Collection class +### _Version 2.0_ +## Use someone else’s Collection class ^ More fully featured, less code to maintain --- +[.background-color: #FFFFFF] + ![120%](images/yay-open-source.jpg) --- @@ -212,12 +221,16 @@ Start using Collections! --- +[.background-color: #FFFFFF] + ![150%](images/all-the-things.jpg) ^ Drupal 8, Sculpin site, PHP libraries --- -## __Thanks!__ +[.header: alignment(center)] -## @opdavies +# Thanks! +### _@opdavies_ +### _oliverdavies.uk_ diff --git a/using-laravel-collections-outside-laravel/2018-08-28-php-south-wales/images/all-the-things.jpg b/using-laravel-collections-outside-laravel/2018-08-28-php-south-wales/images/all-the-things.jpg new file mode 100644 index 0000000..4817029 Binary files /dev/null and b/using-laravel-collections-outside-laravel/2018-08-28-php-south-wales/images/all-the-things.jpg differ diff --git a/using-laravel-collections-outside-laravel/2018-08-28-php-south-wales/images/blog-post.png b/using-laravel-collections-outside-laravel/2018-08-28-php-south-wales/images/blog-post.png new file mode 100644 index 0000000..9805f99 Binary files /dev/null and b/using-laravel-collections-outside-laravel/2018-08-28-php-south-wales/images/blog-post.png differ diff --git a/using-laravel-collections-outside-laravel/2018-08-28-php-south-wales/images/collection-class-module-project-page-1.png b/using-laravel-collections-outside-laravel/2018-08-28-php-south-wales/images/collection-class-module-project-page-1.png new file mode 100644 index 0000000..5faddee Binary files /dev/null and b/using-laravel-collections-outside-laravel/2018-08-28-php-south-wales/images/collection-class-module-project-page-1.png differ diff --git a/using-laravel-collections-outside-laravel/2018-08-28-php-south-wales/images/collection-class-module-project-page-2.png b/using-laravel-collections-outside-laravel/2018-08-28-php-south-wales/images/collection-class-module-project-page-2.png new file mode 100644 index 0000000..a5bd958 Binary files /dev/null and b/using-laravel-collections-outside-laravel/2018-08-28-php-south-wales/images/collection-class-module-project-page-2.png differ diff --git a/using-laravel-collections-outside-laravel/2018-08-28-php-south-wales/images/composer.png b/using-laravel-collections-outside-laravel/2018-08-28-php-south-wales/images/composer.png new file mode 100644 index 0000000..bb95a49 Binary files /dev/null and b/using-laravel-collections-outside-laravel/2018-08-28-php-south-wales/images/composer.png differ diff --git a/using-laravel-collections-outside-laravel/2018-08-28-php-south-wales/images/drupal-8.png b/using-laravel-collections-outside-laravel/2018-08-28-php-south-wales/images/drupal-8.png new file mode 100644 index 0000000..a0d7bb9 Binary files /dev/null and b/using-laravel-collections-outside-laravel/2018-08-28-php-south-wales/images/drupal-8.png differ diff --git a/using-laravel-collections-outside-laravel/2018-08-28-php-south-wales/images/drupal-issue-1.png b/using-laravel-collections-outside-laravel/2018-08-28-php-south-wales/images/drupal-issue-1.png new file mode 100644 index 0000000..1d79637 Binary files /dev/null and b/using-laravel-collections-outside-laravel/2018-08-28-php-south-wales/images/drupal-issue-1.png differ diff --git a/using-laravel-collections-outside-laravel/2018-08-28-php-south-wales/images/drupal-issue-2.png b/using-laravel-collections-outside-laravel/2018-08-28-php-south-wales/images/drupal-issue-2.png new file mode 100644 index 0000000..aabd686 Binary files /dev/null and b/using-laravel-collections-outside-laravel/2018-08-28-php-south-wales/images/drupal-issue-2.png differ diff --git a/using-laravel-collections-outside-laravel/2018-08-28-php-south-wales/images/druplicon.png b/using-laravel-collections-outside-laravel/2018-08-28-php-south-wales/images/druplicon.png new file mode 100644 index 0000000..f8ced55 Binary files /dev/null and b/using-laravel-collections-outside-laravel/2018-08-28-php-south-wales/images/druplicon.png differ diff --git a/using-laravel-collections-outside-laravel/2018-08-28-php-south-wales/images/laravel.png b/using-laravel-collections-outside-laravel/2018-08-28-php-south-wales/images/laravel.png new file mode 100644 index 0000000..3c83acc Binary files /dev/null and b/using-laravel-collections-outside-laravel/2018-08-28-php-south-wales/images/laravel.png differ diff --git a/using-laravel-collections-outside-laravel/2018-08-28-php-south-wales/images/packagist.png b/using-laravel-collections-outside-laravel/2018-08-28-php-south-wales/images/packagist.png new file mode 100644 index 0000000..c129ae3 Binary files /dev/null and b/using-laravel-collections-outside-laravel/2018-08-28-php-south-wales/images/packagist.png differ diff --git a/using-laravel-collections-outside-laravel/2018-08-28-php-south-wales/images/tweet-1.png b/using-laravel-collections-outside-laravel/2018-08-28-php-south-wales/images/tweet-1.png new file mode 100644 index 0000000..e58e63f Binary files /dev/null and b/using-laravel-collections-outside-laravel/2018-08-28-php-south-wales/images/tweet-1.png differ diff --git a/using-laravel-collections-outside-laravel/2018-08-28-php-south-wales/images/yay-open-source.jpg b/using-laravel-collections-outside-laravel/2018-08-28-php-south-wales/images/yay-open-source.jpg new file mode 100644 index 0000000..ab224d2 Binary files /dev/null and b/using-laravel-collections-outside-laravel/2018-08-28-php-south-wales/images/yay-open-source.jpg differ diff --git a/using-laravel-collections-outside-laravel/2018-08-28-php-south-wales/slides.md b/using-laravel-collections-outside-laravel/2018-08-28-php-south-wales/slides.md new file mode 100644 index 0000000..3a522cf --- /dev/null +++ b/using-laravel-collections-outside-laravel/2018-08-28-php-south-wales/slides.md @@ -0,0 +1,246 @@ +theme: poster, 8 +autoscale: true +build-lists: true +header-emphasis: #53B0EB +header: alignment(left) +text: alignment(left) +text-emphasis: #53B0EB +code: Operator Mono, line-height(1.5) + +[.background-color: #FFFFFF] +[.hide-footer] +[.header: #111111, alignment(center)] + +## Using Laravel Collections...
Outside Laravel + +![250%](images/laravel.png) + +--- + +[.header: alignment(center)] + +## Collections :thumbsup: + +^ Became a fan of Collections whilst learning Laravel +Powerful object orientated way to interact with arrays +Store items within the collection, run methods, chainable +More readable, less temporary variables +Video on Laracasts, Adam Wathan's refactoring to Collections +Wanted to use them with different PHP projects e.g. Drupal + +--- + +```php +collect(['foo', 'bar']); // ['foo', 'bar'] + +collect('foobar'); // ['foobar'] + +$object = new stdClass(); +$object->foo = 'bar'; +collect($object); // ['foo' => 'bar'] +collect($object)->get('foo'); // bar +``` + +^ How do you make a collection? +collect function is provided +String, array or object +Stored as items within the Collection object + +--- + +```php +$collection = collect(['a', 'b', 1, 'c', 2, 'd', 'e', 3, 4]); + +$collection->count(); // 9 + +$collection->first(); // a + +$collection->first(function ($item) { + return is_numeric($item); +}); // 1 + +$collection->contains(2); // true + +$collection->contains([2, 10]); // false + +$collection->filter(function ($item) { + return $item > 2; +}); // [3, 4] +``` + +^ Once you have a collection, what can you do with it? +"contains" - no more needle/haystack, haystack/needle +"filter" - filters false, null values +Can pass callbacks to `first` and `filter`, return true or false as needed. + +--- + +```php +$collection = collect([ + ['name' => 'John', 'email' => 'john@example.com', 'age' => 31], + ['name' => 'Jane', 'email' => 'jane@example.com', 'age' => 27], +]); + +$collection->pluck('name'); // ['John', 'Jane'] + +$collection->pluck('name')->sort(); // ['Jane', 'John'] + +$collection->filter(function ($person) { + return $person['age'] >= 30; +})->pluck('name'); // ['John'] +``` + +--- + +[.background-color: #FFFFFF] + +![100%](images/druplicon.png) + +^ This is great, but how can I do that in my Drupal code? +How can I do that? + +--- + +## There’s a module for that! +### _- Drupalers_ + +--- + +[.text: alignment(center)] + +## [fit] There's not a module for that. :disappointed: +### _- Me_ + +--- + +![fit](images/collection-class-module-project-page-1.png) + +^ Drupal 7 + +--- + + +### _Version 1.0_ +## Write my own Collection class + +^ Wrote my own Collection class +Wrote my own tests + +--- + +![](images/drupal-issue-1.png) + +^ Maybe 70% of what Laravel's could do. + +--- + +![](images/packagist.png) + +^ Can't remember how, but then I found this. + +--- + + +### Collect - Illuminate Collections as a separate package. +#### _https://packagist.org/packages/tightenco/collect_ + +--- + +### Import Laravel's Collections into non-Laravel packages easily, without needing to require the entire Illuminate\Support package. +#### _https://packagist.org/packages/tightenco/collect_ + +--- + +[.background-color: #FFFFFF] + +![600%](images/composer.png) + +^ Can install via Composer + +--- + +## _composer require_
tightenco/collect + +--- +![](images/drupal-issue-2.png) + +--- + +![fit](images/collection-class-module-project-page-2.png) + +--- + +### _Version 2.0_ +## Use someone else’s Collection class + +^ More fully featured, less code to maintain + +--- + +[.background-color: #FFFFFF] + +![120%](images/yay-open-source.jpg) + +--- + +[.build-lists: false] + +- Install Composer +- Require `tightenco/collect` +- Include `autoload.php` +- `collect()` away! + +--- + +[.build-lists: false] + +- Install Composer +- Require `tightenco/collect` +- Include `autoload.php` +- `collect()` away! + +![100%](images/drupal-8.png) + +--- + +```php +// index.php + +require __DIR__ . '/vendor/autoload.php'; + +$collection = collect(['foo', 'bar']); + +$collection->each(function ($item) { + // Do something. +}); +``` + +^ Require/include autoload.php +Start using Collections! +`collect` function is autoloaded + +--- + +[.background-color: #FFFFFF] + +![150%](images/all-the-things.jpg) + +^ Drupal 8, Sculpin site, PHP libraries + +--- + +[.background-color: #FFFFFF] + +![inline](images/tweet-1.png) + +--- + +![inline](images/blog-post.png) + +--- + +[.header: alignment(center)] + +# Thanks! +### _@opdavies_ +### _oliverdavies.uk_ diff --git a/using-laravel-collections-outside-laravel/2018-08-28-php-south-wales/slides2.pdf b/using-laravel-collections-outside-laravel/2018-08-28-php-south-wales/slides2.pdf new file mode 100644 index 0000000..6b9bbfe Binary files /dev/null and b/using-laravel-collections-outside-laravel/2018-08-28-php-south-wales/slides2.pdf differ diff --git a/using-laravel-collections-outside-laravel/abstract.md b/using-laravel-collections-outside-laravel/abstract.md deleted file mode 100644 index 85c6aee..0000000 --- a/using-laravel-collections-outside-laravel/abstract.md +++ /dev/null @@ -1,3 +0,0 @@ -# Using Laravel Collections... Outside Laravel - -Laravel Collections are a powerful object-orientated way of interacting with PHP arrays, but did you know that they can be used outside of Laravel, in any PHP project? This short talk shows how we can use Composer to include Laravel Collections within a non-Laravel project and put them to use within your own code.