diff --git a/.github/workflows/continous-integration.yml b/.github/workflows/continous-integration.yml index 9ff26a88..571dd1ac 100644 --- a/.github/workflows/continous-integration.yml +++ b/.github/workflows/continous-integration.yml @@ -22,15 +22,15 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - php: ['8.0', '7.4', '7.3'] - wordpress: ['6.2', '6.1', '6.0'] + php: ['8.1', '8.0', '7.4'] + wordpress: ['6.3', '6.2', '6.1', '6.0'] composer_version: ['v2'] include: - php: '7.4' wordpress: '6.2' coverage: '--coverage --coverage-xml' xdebug: 1 - - php: '7.3' + - php: '8.1' wordpress: '6.1' debug: '--debug' - wordpress: '6.2' diff --git a/.gitignore b/.gitignore index 3b862ddf..ca952782 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,9 @@ node_modules/ .vscode/settings.json .github_changelog_generator vendor/* +!vendor/woographql/ +vendor/woographql/* +!vendor/woographql/.gitkeep !tests tests/*.suite.yml build/ @@ -28,3 +31,4 @@ php-coveralls.phar codeception.yml .editorconfig plugin-build +bin/strauss.phar diff --git a/bin/_lib.sh b/bin/_lib.sh index d5861320..27248db6 100755 --- a/bin/_lib.sh +++ b/bin/_lib.sh @@ -107,7 +107,8 @@ remove_local_test_library() { cleanup_composer_file() { echo "Removing extra config..." - composer config --unset extra + composer config --unset extra.wordpress-install-dir + composer config --unset extra.installer-paths echo "Removing repositories..." composer config --unset repositories @@ -125,7 +126,11 @@ cleanup_local_files() { fi echo "Rebuilding lock file..." - rm -rf $PROJECT_ROOT_DIR/vendor + cd "$PROJECT_ROOT_DIR/vendor" + + find . ! -name '.gitkeep' -type f -exec rm -f {} + + + cd $PROJECT_ROOT_DIR composer install --no-dev } diff --git a/composer.json b/composer.json index 21c67d6a..2210298c 100644 --- a/composer.json +++ b/composer.json @@ -26,16 +26,14 @@ }, "require-dev": { "axepress/wp-graphql-cs": "^1.0.0-beta", - "axepress/wp-graphql-stubs": "1.14.0", - "php-stubs/woocommerce-stubs": "7.5.0", + "axepress/wp-graphql-stubs": "^1.14.0", + "php-stubs/woocommerce-stubs": "^7.5.0", "phpstan/extension-installer": "^1.3", + "phpstan/phpdoc-parser": "^1.22.0", "phpstan/phpstan": "^1.10", "szepeviktor/phpstan-wordpress": "^1.3" }, "config": { - "platform": { - "php": "7.3" - }, "optimize-autoloader": true, "process-timeout": 0, "sort-packages": true, @@ -46,6 +44,11 @@ "composer/installers": true } }, + "autoload": { + "classmap": [ + "vendor/woographql/" + ] + }, "autoload-dev": { "files": [ "tests/_data/config.php" @@ -58,21 +61,32 @@ "installTestEnv": "bash bin/install-test-env.local.sh", "runPreCommitCleanup": "bash bin/cleanup.local.sh", "runWPUnitTest": "vendor/bin/codecept run wpunit", - "dBuild": "env $(sed -e '/^#/d' .env.testing) docker-compose build", - "dRunApp": "env $(sed -e '/^#/d' .env.testing) docker-compose up testable_app app_db mailhog", - "dRunTestingDb": "if [ ! \"$(docker ps -a | grep testing_db)\" ]; then env $(sed -e '/^#/d' .env.testing) docker-compose up -d testing_db; fi", - "dSetMainDb": "docker-compose exec testable_app ./setup-database.sh main", - "dSetTestingDb": "docker-compose exec testable_app wait-for-it -s -t 300 testing_db:3306 -- ./setup-database.sh testing", + "dBuild": "env $(sed -e '/^#/d' .env.testing) docker compose build", + "dRunApp": "env $(sed -e '/^#/d' .env.testing) docker compose up testable_app app_db mailhog", + "dRunTestingDb": "if [ ! \"$(docker ps -a | grep testing_db)\" ]; then env $(sed -e '/^#/d' .env.testing) docker compose up -d testing_db; fi", + "dSetMainDb": "docker compose exec testable_app ./setup-database.sh main", + "dSetTestingDb": "docker compose exec testable_app wait-for-it -s -t 300 testing_db:3306 -- ./setup-database.sh testing", "dRunTest": [ "@dRunTestingDb", "@dSetTestingDb", - "docker-compose exec testable_app wait-for-it -s -t 300 testing_db:3306 -- codecept run $FILTER" + "docker compose exec testable_app wait-for-it -s -t 300 testing_db:3306 -- codecept run $FILTER" ], - "dRunTestStandalone": "env $(sed -e '/^#/d' .env.testing) docker-compose run --rm run_tests", - "dDestroy": "docker-compose down -v", + "dRunTestStandalone": "env $(sed -e '/^#/d' .env.testing) docker compose run --rm run_tests", + "dDestroy": "docker compose down -v", "lint": "vendor/bin/phpcs -vp .", "fix": "vendor/bin/phpcbf -vp .", - "stan": "phpstan analyze --ansi --memory-limit=1G" + "stan": "phpstan analyze --ansi --memory-limit=1G", + "strauss": [ + "test -f ./bin/strauss.phar || curl -o bin/strauss.phar -L -C - https://github.com/BrianHenryIE/strauss/releases/download/0.14.0/strauss.phar", + "@php bin/strauss.phar", + "composer dump-autoload --optimize" + ], + "post-install-cmd": [ + "@strauss" + ], + "post-update-cmd": [ + "@strauss" + ] }, "support": { "issues": "https://github.com/wp-graphql/wp-graphql-woocommerce/issues", @@ -88,7 +102,6 @@ "/plugin-build", "/phpstan", "/tests", - "!/vendor", "/codeception.dist.yml", "/codeception.yml", "/codeclimate.yml", @@ -100,5 +113,21 @@ "/README.md", "/phpstan.neon.dist" ] + }, + "extra": { + "strauss" : { + "target_directory": "vendor/woographql", + "namespace_prefix": "WPGraphQL\\WooCommerce\\Vendor\\", + "classmap_prefix": "WP_GraphQL_WooCommerce_Vendor", + "constant_prefix": "WPGRAPHQL_WOOCOMMERCE_VENDOR", + "delete_vendor_files": true, + "include_modified_date": false, + "packages" : [ + "firebase/php-jwt" + ], + "exclude_from_prefix": { + "file_patterns": [] + } + } } } diff --git a/composer.lock b/composer.lock index e3cc052a..59476f79 100644 --- a/composer.lock +++ b/composer.lock @@ -4,29 +4,29 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "80fff7fe58495246e8a37a729ee89164", + "content-hash": "952227d1890f30e28c92df40f690135a", "packages": [ { "name": "firebase/php-jwt", - "version": "v6.4.0", + "version": "v6.8.1", "source": { "type": "git", "url": "https://github.com/firebase/php-jwt.git", - "reference": "4dd1e007f22a927ac77da5a3fbb067b42d3bc224" + "reference": "5dbc8959427416b8ee09a100d7a8588c00fb2e26" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/firebase/php-jwt/zipball/4dd1e007f22a927ac77da5a3fbb067b42d3bc224", - "reference": "4dd1e007f22a927ac77da5a3fbb067b42d3bc224", + "url": "https://api.github.com/repos/firebase/php-jwt/zipball/5dbc8959427416b8ee09a100d7a8588c00fb2e26", + "reference": "5dbc8959427416b8ee09a100d7a8588c00fb2e26", "shasum": "" }, "require": { - "php": "^7.1||^8.0" + "php": "^7.4||^8.0" }, "require-dev": { "guzzlehttp/guzzle": "^6.5||^7.4", - "phpspec/prophecy-phpunit": "^1.1", - "phpunit/phpunit": "^7.5||^9.5", + "phpspec/prophecy-phpunit": "^2.0", + "phpunit/phpunit": "^9.5", "psr/cache": "^1.0||^2.0", "psr/http-client": "^1.0", "psr/http-factory": "^1.0" @@ -65,9 +65,9 @@ ], "support": { "issues": "https://github.com/firebase/php-jwt/issues", - "source": "https://github.com/firebase/php-jwt/tree/v6.4.0" + "source": "https://github.com/firebase/php-jwt/tree/v6.8.1" }, - "time": "2023-02-09T21:01:23+00:00" + "time": "2023-07-14T18:33:00+00:00" } ], "packages-dev": [ @@ -185,16 +185,16 @@ }, { "name": "axepress/wp-graphql-stubs", - "version": "v1.14.0", + "version": "v1.14.6", "source": { "type": "git", "url": "https://github.com/AxeWP/wp-graphql-stubs.git", - "reference": "1455a46043f758b77a49337c9520cb79c5a0a31f" + "reference": "9de2d036497a470354ac541196806eb426c67740" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/AxeWP/wp-graphql-stubs/zipball/1455a46043f758b77a49337c9520cb79c5a0a31f", - "reference": "1455a46043f758b77a49337c9520cb79c5a0a31f", + "url": "https://api.github.com/repos/AxeWP/wp-graphql-stubs/zipball/9de2d036497a470354ac541196806eb426c67740", + "reference": "9de2d036497a470354ac541196806eb426c67740", "shasum": "" }, "require": { @@ -225,7 +225,7 @@ ], "support": { "issues": "https://github.com/AxeWP/wp-graphql-stubs/issues", - "source": "https://github.com/AxeWP/wp-graphql-stubs/tree/v1.14.0" + "source": "https://github.com/AxeWP/wp-graphql-stubs/tree/v1.14.6" }, "funding": [ { @@ -233,7 +233,7 @@ "type": "github" } ], - "time": "2023-03-09T01:45:16+00:00" + "time": "2023-07-02T01:57:05+00:00" }, { "name": "dealerdirect/phpcodesniffer-composer-installer", @@ -312,16 +312,16 @@ }, { "name": "php-stubs/woocommerce-stubs", - "version": "v7.5.0", + "version": "v7.8.2", "source": { "type": "git", "url": "https://github.com/php-stubs/woocommerce-stubs.git", - "reference": "9a735c3a66fb3eab4bdbb9e998db00b1fbd268d7" + "reference": "185d4281e97aa37d193334e5b8da007652f9d391" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-stubs/woocommerce-stubs/zipball/9a735c3a66fb3eab4bdbb9e998db00b1fbd268d7", - "reference": "9a735c3a66fb3eab4bdbb9e998db00b1fbd268d7", + "url": "https://api.github.com/repos/php-stubs/woocommerce-stubs/zipball/185d4281e97aa37d193334e5b8da007652f9d391", + "reference": "185d4281e97aa37d193334e5b8da007652f9d391", "shasum": "" }, "require": { @@ -350,9 +350,9 @@ ], "support": { "issues": "https://github.com/php-stubs/woocommerce-stubs/issues", - "source": "https://github.com/php-stubs/woocommerce-stubs/tree/v7.5.0" + "source": "https://github.com/php-stubs/woocommerce-stubs/tree/v7.8.2" }, - "time": "2023-03-14T14:19:48+00:00" + "time": "2023-07-03T21:07:37+00:00" }, { "name": "php-stubs/wordpress-stubs", @@ -619,16 +619,16 @@ }, { "name": "phpstan/phpdoc-parser", - "version": "1.22.1", + "version": "1.23.0", "source": { "type": "git", "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "65c39594fbd8c67abfc68bb323f86447bab79cc0" + "reference": "a2b24135c35852b348894320d47b3902a94bc494" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/65c39594fbd8c67abfc68bb323f86447bab79cc0", - "reference": "65c39594fbd8c67abfc68bb323f86447bab79cc0", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/a2b24135c35852b348894320d47b3902a94bc494", + "reference": "a2b24135c35852b348894320d47b3902a94bc494", "shasum": "" }, "require": { @@ -660,9 +660,9 @@ "description": "PHPDoc parser with support for nullable, intersection and generic types", "support": { "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/1.22.1" + "source": "https://github.com/phpstan/phpdoc-parser/tree/1.23.0" }, - "time": "2023-06-29T20:46:06+00:00" + "time": "2023-07-23T22:17:56+00:00" }, { "name": "phpstan/phpstan", @@ -1107,8 +1107,5 @@ "php": ">=7.2" }, "platform-dev": [], - "platform-overrides": { - "php": "7.3" - }, "plugin-api-version": "2.3.0" } diff --git a/docs/development-w-docker.md b/docs/development-w-docker.md new file mode 100644 index 00000000..7cb9ae25 --- /dev/null +++ b/docs/development-w-docker.md @@ -0,0 +1,10 @@ +--- +title: "Development w/ Docker" +author: "Geoff Taylor" +description: "A simple to follow guide on using the WooGraphQL App Docker Image." +keywords: "" +--- + +# Coming Soon + +Sorry, this section is still under development :construction:. \ No newline at end of file diff --git a/docs/images/image-01.png b/docs/images/image-01.png new file mode 100644 index 00000000..8252b657 Binary files /dev/null and b/docs/images/image-01.png differ diff --git a/docs/images/image-02.png b/docs/images/image-02.png new file mode 100644 index 00000000..cd23ca12 Binary files /dev/null and b/docs/images/image-02.png differ diff --git a/docs/images/image-03.png b/docs/images/image-03.png new file mode 100644 index 00000000..e02aa865 Binary files /dev/null and b/docs/images/image-03.png differ diff --git a/docs/images/image-04.png b/docs/images/image-04.png new file mode 100644 index 00000000..4c9fcbc2 Binary files /dev/null and b/docs/images/image-04.png differ diff --git a/docs/images/image-05.png b/docs/images/image-05.png new file mode 100644 index 00000000..6a952da2 Binary files /dev/null and b/docs/images/image-05.png differ diff --git a/docs/images/image-06.png b/docs/images/image-06.png new file mode 100644 index 00000000..f0f1d4ea Binary files /dev/null and b/docs/images/image-06.png differ diff --git a/docs/images/image-07.png b/docs/images/image-07.png new file mode 100644 index 00000000..b2e8c91c Binary files /dev/null and b/docs/images/image-07.png differ diff --git a/docs/local-testing.md b/docs/local-testing.md new file mode 100644 index 00000000..efd14dbc --- /dev/null +++ b/docs/local-testing.md @@ -0,0 +1,461 @@ +--- +title: "Local Testing" +author: "Geoff Taylor" +description: "An extensive guide on local testing w/ WooGraphQL.", +keywords: "" +--- + +# Local CLI Testing + +Editing the **WPGraphQL** API can be tricky at times, even more so when dealing with a massive plugin like WooCommerce. +Don't let this discourage you though, it's possible simplify this development process with some application of **Test-Driven Development (TDD)**. Now if you've ever been told anything about **TDD**, it's probably that **TDD** doesn't always fit everyone's development process. Nonetheless, the argument I'm trying to make is here is that using **TDD** and following this guide, you'll learn how to make proper changes to the **WPGraphQL/WooGraphQL** schema as well as write code that you know works regardless of where the GraphQL request came from or where the WPGraphQL server is installed. + +## Codeception & the wp-browser module + +**WPGraphQL** and **WooGraphQL** both use the **[Codeception](https://codeception.com/)** testing framework alongside the **[wp-browser](https://wpbrowser.wptestkit.dev/)** module created by [Luca Tumedei](https://www.theaveragedev.com/) for running the automated test suite. We'll be using Codeception scaffolding to generate all the tedious test code, but this will not be an in-depth guide on either of these libraries. It's not required to process with this tutorial, but it's highly recommended that after finishing this tutorial you take a look at the documentation for both. + +- **[Codeception](https://codeception.com/docs/01-Introduction)** +- **[wp-browser](https://wpbrowser.wptestkit.dev/)** + +## Setting up WordPress for testing + +Before we can begin testing we need a local WordPress installation. If you already have a local installation for development that you wish to use, make a backup database for testing and skip to **[Setting up Codeception](#settingupcodeception)**. If you don't have a local installation or simply don't want to risk your local installation, you can use the scripts provided by **WPGraphQL** and **WooGraphQL** to create one for testing in a temporary directory. + +### Prerequisties + +Have **PHP**, **MySQL** or **PostgreSQL**, **Composer**, and **[WP-CLI](https://wp-cli.org/)** installed as well as terminal/shell/command-line access. + +1. Start by cloning **[WooGraphQL](https://github.com/wp-graphql/wp-graphql-woocommerce)**. +2. Open your terminal. +3. Copy the `.env.dist` to `.env` by execute the following in your terminal in the **WooGraphQL** root directory. + + ```bash + cp .env.dist .env + ``` + +4. Open the .env and update the highlighted environmental variables to match your machine setup. +![.env example](images/image-01.png) +5. Last thing to do is run the WordPress testing environment install script in the terminal. + + ```bash + composer install-test-env + ``` + +This will create and configure a WordPress installation in a temporary directory for the purpose of testing. + +## Setting up Codeception + +Now that we have setup our testing environment, let's run the tests. To do this we will need to install the **Codeception** and the rest of our **devDependencies** + +1. First run `composer install` in the terminal. +2. Next copy the `codeception.dist.yml` to `codeception.yml` + + ```bash + cp codeception.dist.yml codeception.yml + ``` + +3. Open `codeception.yml` and make the following changes. +![codeception.yml params config](images/image-02.png) +![codeception.yml WPLoader config](images/image-03.png) + +Now you all set to run the tests. + +## Running the tests + +Now we're ready to get started with testing. There is a small issue you may have with our testing environment. The WordPress installation we created doesn't support **end-to-end (*e2e*)** testing, however this won't be a problem. **WPGraphQL** is an API and most of the time you can get away with just ensuring that your query works, and **WPGraphQL** provides a few functions that will allow us to do just that. + +Well, let's get started by running all the unit tests. Back in your terminal run the following: + +```bash +vendor/bin/codecept run wpunit +``` + +If everything is how it should be you should get all passing tests. +![WPUnit test results](images/image-04.png) + +## Writing your first WooGraphQL and WPGraphQL WPUnit test + +This rest of this guide walk through creating a competent WPUnit test and implemented the functionality needed to ensure that test passed. For the most part everything used here can be used when making changes to WPGraphQL as well as many of the WPGraphQL extensions created by @jasonbahl, myself and the WPGraphQL community. + +The functionality we'll be adding in the coming steps will be to add the **Integer** field `itemCount` on the **Cart** object type. To do this we'll be. + +1. **Generating a WPUnit test file** Now typically for a feature so small it would be enough to update the first test in the **CartQueriesTest** class to include the desired `itemCount` field, however to the purpose of this guide we'll be creating a new test file named **ItemCountTest**. +2. **Writing our test** The name says it all. +3. **Run the test expecting failure** The purpose of this step will be used to introduce to **WPGraphQL**'s Error Reporting and the `codecept_debug` function. +4. **Implementing our changes** This step will do some exploring into how **WooGraphQL** and **WPGraphQL** work behind the scenes, and diving in to some key components. After acquiring a grasp of WPGraphQL execution implementing the desired changes with be trivially. +5. **Run test expecting success** The final step will be to the **ItemCountTest** looking for success this time. + +### Generating a WPUnit test file + +The PHP testing suite used by WPGraphQL and WooGraphQL is Codeception, but they don't manage the `codeception/codeception` in **Composer**. That is done by the `lucatume/wp-browser` package. This package, developed and maintained by *[theAverageDev](http://theaveragedev.com/)* [Luca Tumedei](https://github.com/lucatume), **[wp-browser](https://wpbrowser.wptestkit.dev/)** is a suite of Codeception modules that provide tools designed specifically for testing WordPress sites, themes, and plugins on multiple levels. The `lucatume/wp-browser` package functions as a one-stop shop managing Codeception and all it's dependencies for WPGraphQL and many of it's extensions. + +So having done everything above, and finally being ready for development, begin by generating the **ItemCountTest** test file with Codeception `generate` command. Run the following in your terminal from the project root directory + +```bash +vendor/bin/codecept generate:wpunit wpunit "ItemCount" +``` + +This will generate a new test file at `tests/wpunit/ItemCountTest.php`. The `generate` is an easy-to-use tool of convience. You learn more about [here](https://codeception.com/for/wordpress). + +The `ItemCountTest.php` file should be a familiar site to anyone whose used Codeception or PHPUnit *(which Codeception is built on)*. + +```php +assertEquals( array( 5 ), [ 5 ] ); +``` + +### Writing our test + +Making changes to GraphQL API is always a rather top-down affair, meaning you'll have an idea of how you want the query to look before you have an idea of how you want the implementation to look. For example the query in relation to the changes we want to make will be as follows. + +```graphql +query { + cart { + contents { + nodes { + quantity + } + } + itemCount + } +} +``` + +Nothing special, but if you sent this query to the WPGraphQL server you'd get back. + +```json +{ + "errors": [ + { + "message": "Cannot query field \"itemCount\" on type \"Cart\".", + "extensions": { + "category": "graphql" + }, + "locations": [ + { + "line": 8, + "column": 5 + } + ] + } + ], +} +``` + +Which shouldn't be surprising, in the next section we'll be taking this query and creating our test around it. + + +> If you didn't already know, an `itemCount` field already exist. It just happens to be under the `contents` connection as a field you can access it like this. +>```graphql +>query { +> cart { contents { itemCount } } +>} +>``` + +#### WooGraphQL Codeception Helpers + +WooCommerce is a vast WordPress plugin with a lot moving parts and getting them all to play nice can be a daunting task. To address this **WooGraphQL** provides a number of helpers for creating just the right scenario for testing our queries. In this guide you'll be expose to the `cart` and `product` helpers, but there are quite a few. However documentation on them is pretty non-existant at the time of creation for this guide. Until this is rectified, it's recommended that you view helper [files](https://github.com/wp-graphql/wp-graphql-woocommerce/tree/develop/tests/_support/Helper/crud-helpers) directly to get a general idea of what they are and their capabilities. + +#### Our setUp() + +Let's finish begin writing out test by creating our scenario with the `setUp()`. Our scenario for this is test is rather simple, our query just need some products and those products have to be in the cart. Using `product` and `cart` helpers we can do this in a few lines of code. + +```php +public function setUp() +{ + // before + parent::setUp(); + + $this->product = $this->getModule( '\Helper\Wpunit' )->product(); + $this->cart = $this->getModule( '\Helper\Wpunit' )->cart(); + $this->products = array( + array( 'product_id' => $this->product->create_simple(), 'quantity' => 1 ), + array( 'product_id' => $this->product->create_simple(), 'quantity' => 1 ), + array( 'product_id' => $this->product->create_simple(), 'quantity' => 2 ), + array( 'product_id' => $this->product->create_simple(), 'quantity' => 3 ), + ); + $this->cart->add( ...$this->products ); +} +``` + +And that's it, scenario created. You maybe confused, but we'll break it down. + +```php +$this->product = $this->getModule( '\Helper\Wpunit' )->product(); +$this->cart = $this->getModule( '\Helper\Wpunit' )->cart(); +``` + +This just assigned the `product` and `cart` helpers to simple reusable class members for later use in the coming test and the rest of the `setUp()`. + +```php +$this->products = array( + array( 'product_id' => $this->product->create_simple(), 'quantity' => 1 ), + array( 'product_id' => $this->product->create_simple(), 'quantity' => 1 ), + array( 'product_id' => $this->product->create_simple(), 'quantity' => 2 ), + array( 'product_id' => $this->product->create_simple(), 'quantity' => 3 ), +); +``` + +The `$product` array holds the `product_id`s and `quantity`s of products being add to our cart. `create_simple( $args = array() )` creates a new simple product with a random name and price and returns the `product_id` of the newly created product. There are `create_external( $args = array() )`, `create_grouped( $args = array() )` and `create_variable( $args = array() )`, as well as many more create functions for creating other objects related to products. + +```php +$this->cart->add( ...$this->products ); +``` + +And finally, you have probably figured out that this adds the products in `$products` to the cart. `cart` helper functions as a glorified wrapper for the `WC()->cart` instance with a extra features for testing. + +Now that our scenario is set. Let's get to are test. We'll start by change the name of the test to `testItemCountField` and assign our query to a string variable. + +```php +public function testItemCountField() +{ + $query = ' + query { + cart { + contents { + nodes { + quantity + } + } + itemCount + } + } + '; +} +``` + +Simple enough, next we'll run our query through WPGraphQL using `graphql( $request_data = [] )`. + +```php +public function testItemCountField() +{ + ... + + $actual = graphql( array( 'query' => $query ) ); + + // use --debug flag to view. + codecept_debug( $actual ); +} +``` + +`graphql()` is a function provided by WPGraphQL. It will process a GraphQL request and return the results as an associative array. This makes it a great tool for testing our queries. + +```php +// use --debug flag to view. +codecept_debug( $actual ); +``` + +If you've taken a look at any of the other tests in WooGraphQL you may have noticed this snippet of code every always every `graphql()` call. `codecept_debug( $data )` is a debug function that dumps the value of `$data` to console. This dump information can viewed by using the `--debug` flag when using Codeception's `run` command. Its use here is great because when we run our test later in debug mode we'll know exactly what WPGraphQL is returning for our query. + +The last step in our test is to confirm the we received the correct values for our `itemCount` field. + +```php +public function testItemCountField() +{ + ... + + $expected = array_sum( array_column( $this->products, 'quantity' ) ); + $this->assertEquals( $expected, $actual['data']['cart']['itemCount'] ); +} +``` + +And that's our test. + +```php +product = $this->getModule('\Helper\Wpunit')->product(); + $this->cart = $this->getModule('\Helper\Wpunit')->cart(); + $this->products = array( + array( 'product_id' => $this->product->create_simple(), 'quantity' => 1 ), + array( 'product_id' => $this->product->create_simple(), 'quantity' => 1 ), + array( 'product_id' => $this->product->create_simple(), 'quantity' => 2 ), + array( 'product_id' => $this->product->create_simple(), 'quantity' => 3 ), + ); + $this->cart->add(...$this->products); + } + + public function tearDown() + { + // your tear down methods here + + // then + parent::tearDown(); + } + + // tests + public function testItemCountField() + { + $query = ' + query { + cart { + contents { + nodes { + quantity + } + } + itemCount + } + } + '; + + $actual = graphql( array( 'query' => $query ) ); + + // use --debug flag to view. + codecept_debug( $actual ); + + $expected = array_sum( array_column( $this->products, 'quantity' ) ); + $this->assertEquals( $expected, $actual['data']['cart']['itemCount'] ); + } +} +``` + +### Run the test expecting failure + +Now that the test is created, we will run it expecting failure and using the `--debug` flag. + +```bash +vendor/bin/codecept run wpunit ItemCountTest --debug +``` + +Running this statement in the terminal will make Codeception run just the `ItemCountTest` in debug mode. This way we can see exactly WPGraphQL returns for our query. + +![test failure results](images/image-05.png) +Running the test will result in the error above. **Cannot query field "itemCount" on type "Cart".** The error is quite easy to understand. We cannot query a field that doesn't yet exist. + +### Implementing our changes + +Before when jump into the code, lets discuss how WPGraphQL and WooGraphQL process requests. + +#### How WPGraphQL works + +When a request is made to Wordpress, during the `after_setup_theme` action **WPGraphQL** initializes the [`Router`](https://github.com/wp-graphql/wp-graphql/blob/develop/src/Router.php) class which is in-charge of determining whether the request is a GraphQL request and acting accordingly. +If the request is a GraphQL request, a [`Request`](https://github.com/wp-graphql/wp-graphql/blob/develop/src/Request.php) class instance is created. It's job is to load the schema and process the request. +The first part of loading the schema and our focal point for purpose is the initialization of the [`TypeRegistry`](https://github.com/wp-graphql/wp-graphql/blob/develop/src/Registry/TypeRegistry.php) and specific the execution of [`graphql_register_types`](https://github.com/wp-graphql/wp-graphql/blob/develop/src/Registry/TypeRegistry.php#L397) action. This hook serves the purpose of providing a location to register types not defined by **WPGraphQL**. + +#### How WooGraphQL works + +WooGraphQL uses `graphql_register_types` to register WooCommerce specific types. +If you're familiar WordPress, you're most like familiar with Custom Post-types _(CPTs)_ the core data object used by WordPress. If you're not too familiar with WooCommerce, _(or even if you are)_, you maybe wondering why the CPTs in WooCommerce have such a different schema shape the CPTs defined by **WPGraphQL**. This due to the fact the WooCommerce wraps it's CPT objects, _(WP_Post)_, in data stores objects. This data stores provide decorator functionality that is widely used by WooCommerce extension. **WooGraphQL** uses data stores as the source for it CPT schema shapes. All other types like the **cart** and **shipping zones** are sourced by custom data objects saved in custom databases, and accessed using WooCommerce built-in functionality. + +#### Implementing our changes + +Now to implement our changes we simply need to register our `itemCount` field to the `Cart` object type. + +```php +register_graphql_field( + 'Cart', + 'itemCount', + array( + 'type' => 'Int', + 'description' => __( 'Total number of items in the cart.', 'wp-graphql-woocommerce' ), + 'resolve' => function( \WC_Cart $source ) { + $items = $source->get_cart() ); + if ( empty( $items ) ) { + return 0; + } + + return array_sum( array_column( $items, 'quantity' ) ); + }, + ), +); +``` + +Simple enough, right. Now we can slap this at the end of the `TypeRegistry` and it would fine, however for a pull request that wouldn't work. So let's include this the inside the `register_graphql_object_type()` call for the `Cart` type with the rest of `Cart` fields. This call can be found in `Cart_Type::register_cart()` in `includes/type/object/class-cart-type.php`. + +```php + /** + * Registers Cart type + */ + public static function register_cart() { + register_graphql_object_type( + 'Cart', + array( + 'description' => __( 'The cart object', 'wp-graphql-woocommerce' ), + 'fields' => array( + ... // Other cart fields + 'itemCount' => array( + 'type' => 'Int', + 'description' => __( 'Total number of items in the cart.', 'wp-graphql-woocommerce' ), + 'resolve' => function( \WC_Cart $source ) { + $items = $source->get_cart(); + if ( empty( $items ) ) { + return 0; + } + + return array_sum( array_column( $items, 'quantity' ) ); + }, + ), + ), + ) + ); + } +``` + +And :boom:! Changes made. Let's run our test!! + +### Run test expecting success + +Now if you re-run `vendor/bin/codecept run wpunit ItemCountTest` +![test successful results](images/image-06.png) + +And we have passed, and if we want to see our query response use the `--debug` flag +![test successful query data](images/image-07.png) + + +## Going Forward + +If you have already, you should at least take a look at the following + +- Documentation on extending **WPGraphQL**. [Here](https://docs.wpgraphql.com/extending/types/). +- **WooCommerce** REST API [Docs](https://woocommerce.github.io/woocommerce-rest-api-docs), seeing as it has been and continues to be the base template for the **WooGraphQL** schema. + +If you plan on contributing you should see the following as both **WPGraphQL** and **WooGraphQL** uses them. + +- **Codeception** [Docs](https://codeception.com) +- **wp-browser** [Docs](https://wpbrowser.wptestkit.dev/) \ No newline at end of file diff --git a/docs/testing-quick-start.md b/docs/testing-quick-start.md new file mode 100644 index 00000000..79e2e102 --- /dev/null +++ b/docs/testing-quick-start.md @@ -0,0 +1,108 @@ +--- +title: "Testing (Quick-Start Guide)" +author: "Geoff Taylor" +description: "A simple guide to get started testing with WooGraphQL." +keywords: "" +--- + +# Testing (Quick-Start Guide) + +## WPUnit Tests + +Until the documentation is in full effect, it's recommended that a [GraphiQL](https://github.com/graphql/graphiql)-based tool like [WPGraphiQL](https://github.com/wp-graphql/wp-graphiql) be used to view the GraphQL schema, an alternative to this is viewing the unit tests located in `tests/wpunit` directory. Which are constantly updated along with the project. If you're interested in contributing when I begin accepting contribution or simply want to run the tests. Follow the instruction below. + +### Prerequisties + +- Shell/CMD access +- [Composer](https://getcomposer.org/) +- [WP-CLI](https://wp-cli.org/) + +### Setup + +1. Make sure all dependencies are install by running `composer install` from the CMD/Terminal in the project directory. +2. Next the copy 2 distributed files with the `.dist` in there filenames. For instance `.env.dist` becomes `.env` and `wpunit.suite.dist.yml` becomes `wpunit.suite.yml`. The distributed files and what their copied names should are as follows. + + - `codeception.dist.yml` => `codeception.yml` + - `.env.dist` => `.env` + +3. Next open `.env` and alter to make you usage. + + ```shell + # docker ENV variables + DB_NAME=wordpress + DB_HOST=app_db + DB_USER=wordpress + DB_PASSWORD=wordpress + WP_TABLE_PREFIX=wp_ + WP_URL=http://localhost + WP_DOMAIN=localhost + ADMIN_EMAIL=admin@example.com + ADMIN_USERNAME=admin + ADMIN_PASSWORD=password + ADMIN_PATH=/wp-admin + + # local codeception/install-wp-tests ENV variables + TEST_DB_NAME=woographql_tests + TEST_DB_HOST=127.0.0.1 + TEST_DB_USER=wordpress + TEST_DB_PASSWORD=wordpress + TEST_WP_TABLE_PREFIX=wp_ + + # install-wp-tests ENV variables + SKIP_DB_CREATE=false + TEST_WP_ROOT_FOLDER=/tmp/wordpress + TEST_ADMIN_EMAIL=admin@wp.test + + # codeception ENV variables + TESTS_DIR=tests + TESTS_OUTPUT=tests/_output + TESTS_DATA=tests/_data + TESTS_SUPPORT=tests/_support + TESTS_ENVS=tests/_envs + ``` + + - `docker ENV variables`: variables defined for use in the Docker/Docker-Compose setups. These are also used in `codeception.dist.yml` for testing within a Docker container. It's recommend that this file be left unchanged and a `codeception.yml` be created for local codeception unit testing. + - `local codeception/install-wp-tests ENV variables`: variable defined for use with codeception testing w/o docker and the `install-wp-tests` script in the `bin` directory. As mentioned above a `codeception.yml` should be created from `codeception.dist.yml` and the variables in the `WPLoader` config should be set accordingly. + - `install-wp-tests ENV variables`: variables specific to the `install-wp-tests` script. The script can be run using `composer install-wp-tests` in the terminal from project directory. + - `codeception ENV variables`: variables used by codeception. This includes within the docker container as well. + +4. Once you have finish modifying the `.env` file. Run `composer install-wp-tests` from the project directory. +5. Upon success you can begin running the tests. + +### Running tests + +To run test use the command `vendor/bin/codecept run [suite [test [:test-function]]]`. +If you use the command with at least a `suite` specified, **Codeception** will run all tests, however this is not recommended. Running a suite `vendor/bin/codecept run wpunit` or a test `vendor/bin/codecept run CouponQueriesTest` is recommended. Running a single `test-function` like `vendor/bin/codecept run ProductQueriesTest:testProductsQueryAndWhereArgs` is also possible. + +To learn more about the usage of Codeception with WordPress view the [Documentation](https://codeception.com/for/wordpress) + +## Functional and Acceptance Tests (Docker & Docker-Compose required) + +It's possible to run functional and acceptance tests, but is very limited at the moment. The script docker entrypoint script runs all three suites (acceptance, functional, and wpunit) at once. This will change eventually, however as of right now, this is the limitation. + +### Running tests + +Even though the two suites use a Docker environment to run, the `testing` service in the `docker.compose.yml` file requires the `.env.dist` and `codeception.dist.yml` untouched. +Run the following in the terminal to run all three suites. Isolating specific suites should be simple to figure out. + +```bash +docker-compose run --rm \ +-e SUITES=acceptance;wpunit;functional \ +-e DEBUG=1 -e COVERAGE=1 testing --scale app=0 +``` + +- The `COVERAGE`, and `DEBUG` vars are optional flags for toggle codecoverage and debug output. +- `--scale app=0` ensures that the service running a local app doesn't create any instances. It must be added or a collision with `mysql` will occur. More on this service in the next section + +## Using docker-compose to run a local installation for live testing. + +This is rather simple just like with testing using docker ensure that `env.dist` and `codeception.dist.yml` are untouched. + +1. Run `docker-compose up --scale testing=0 app` +2. wait for `app_1 | Success: Exported to '/var/www/html/wp-content/plugins/wp-graphql-woocommerce/tests/_data/dump.sql'.` to print to the terminal. +3. navigate to `http://localhost:8091`. And that's it. + +You can view the configuration for the installation in the `docker-compose.yml`. +**NOTE: if you get redirected to `http://localhost` run `docker-compose down` to remove any existing containers related to the project, then re-run Step 1.** + +- For more information about the docker-image uses in the service, it's on [Docker Hub](https://hub.docker.com/r/kidunot89/woographql-app). \ No newline at end of file diff --git a/docs/testing-w-docker.md b/docs/testing-w-docker.md new file mode 100644 index 00000000..b0de5223 --- /dev/null +++ b/docs/testing-w-docker.md @@ -0,0 +1,10 @@ +--- +title: "Testing w/ Docker" +author: "Geoff Taylor" +description: "An extensive guide on testing w/ WooGraphQL + Docker." +keywords: "" +--- + +# Coming Soon + +Sorry, this section is still under development :construction:. \ No newline at end of file diff --git a/docs/toc.md b/docs/toc.md index fa1e3e8a..22f507ed 100644 --- a/docs/toc.md +++ b/docs/toc.md @@ -25,6 +25,12 @@ - [Using Product Bundle Data + Mutations](using-product-bundle-data-and-mutations.md) - [Using Product Add-ons Data + Mutations](using-product-addons-data-and-mutations.md) -## Other +## Contributing + +- [Testing Quick-Start Guide](testing-quick-start.md) +- [Local CLI Testing](local-testing.md) +- [Development with Docker](development-w-docker.md) +- [CLI Testing with Docker](testing-w-docker.md) +## Glossary - [Schema Docs](https://woographql.com/schema) \ No newline at end of file diff --git a/docs/using-customer-data-and-mutations.md b/docs/using-customer-data-and-mutations.md index 8b2b9854..a4729fe6 100644 --- a/docs/using-customer-data-and-mutations.md +++ b/docs/using-customer-data-and-mutations.md @@ -422,6 +422,6 @@ You'll note these samples also don't take into account React server components. ## Conclusion -In this guide, we've seen how to use the WooGraphQL API to create a clone of WooCommerce's user account pages. We've used the `customer` query to fetch the user's details, the `orders` field on the `customer` type to fetch the user's orders, and the `updateCustomer` mutation to update the user's details and addresses. We've also seen how to handle user authentication with the `login` mutation and how to log out the user. It should be noted that this was all made possible by proper usage our `SessionProvider`. +In this section, we've seen how to use the WooGraphQL API to create a clone of WooCommerce's user account pages. We've used the `customer` query to fetch the user's details, the `orders` field on the `customer` type to fetch the user's orders, and the `updateCustomer` mutation to update the user's details and addresses. We've also seen how to handle user authentication with the `login` mutation and how to log out the user. It should be noted that this was all made possible by proper usage our `SessionProvider`. With the completion of this section and all proceeding sections, you have been given an deep dive into the basic and intermediate usages of WooGraphQL. The following sections will be on WooGraphQL Pro functionality. diff --git a/docs/using-product-addons-data-and-mutations.md b/docs/using-product-addons-data-and-mutations.md index 8ef37fae..02dc9d25 100644 --- a/docs/using-product-addons-data-and-mutations.md +++ b/docs/using-product-addons-data-and-mutations.md @@ -1,6 +1,10 @@ --- title: "Using Product Add-ons Data + Mutations with WooGraphQL" +author: "Geoff Taylor" description: "Learn how to use the Product Add-on functionality with WooGraphQL by building upon the code from `Using Product Data` and `Creating Session Provider and using Cart Mutations`." keywords: "WooGraphQL, WPGraphQL, WooCommerce, GraphQL, Product Add-on functionality, Product Data, Session Provider, Cart Mutations" -author: "Geoff Taylor" --- + +# Coming Soon + +Sorry, this section is still under development :construction:. \ No newline at end of file diff --git a/includes/class-wp-graphql-woocommerce.php b/includes/class-wp-graphql-woocommerce.php index 6d1c9d32..1a538fc9 100644 --- a/includes/class-wp-graphql-woocommerce.php +++ b/includes/class-wp-graphql-woocommerce.php @@ -356,7 +356,7 @@ if ( ! class_exists( '\WPGraphQL\WooCommerce\WP_GraphQL_WooCommerce' ) ) : * detected. This likely means the user cloned the repo from Github * but did not run `composer install` */ - if ( ! class_exists( 'Firebase\JWT\JWT' ) ) { + if ( ! class_exists( 'WPGraphQL\WooCommerce\Vendor\Firebase\JWT\JWT' ) ) { add_action( 'admin_notices', static function () { diff --git a/includes/utils/class-ql-session-handler.php b/includes/utils/class-ql-session-handler.php index 3f18a8a6..a71fa422 100644 --- a/includes/utils/class-ql-session-handler.php +++ b/includes/utils/class-ql-session-handler.php @@ -8,9 +8,9 @@ namespace WPGraphQL\WooCommerce\Utils; -use Firebase\JWT\JWT; -use Firebase\JWT\Key; use WC_Session_Handler; +use WPGraphQL\WooCommerce\Vendor\Firebase\JWT\JWT; +use WPGraphQL\WooCommerce\Vendor\Firebase\JWT\Key; /** * Class - QL_Session_Handler diff --git a/phpcs.xml.dist b/phpcs.xml.dist index 6ba58b3a..6361c309 100644 --- a/phpcs.xml.dist +++ b/phpcs.xml.dist @@ -78,4 +78,40 @@ + + + + + + + + + + + + + + + /tests/ + + + + + /tests/ + + + /tests/ + + + /tests/ + + + /tests/ + + + /tests/ + + + /tests/ + diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 12ca252e..11cd3a5f 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -15,6 +15,7 @@ parameters: - access-functions.php - includes/ scanDirectories: + - vendor/woographql/ - local/public/wp-content/plugins/wp-graphql-jwt-authentication/ - local/public/wp-content/plugins/woocommerce/src/Internal/DataStores/Orders/ scanFiles: diff --git a/tests/functional/ProtectedRouterCest.php b/tests/functional/ProtectedRouterCest.php index 7be45362..b377405e 100644 --- a/tests/functional/ProtectedRouterCest.php +++ b/tests/functional/ProtectedRouterCest.php @@ -1,6 +1,6 @@