diff --git a/.env.dist b/.env.dist new file mode 100644 index 00000000..af3567e9 --- /dev/null +++ b/.env.dist @@ -0,0 +1,54 @@ +# COPY THIS TO .env and change the values + +# Used to create the WP install. +DB_NAME=wordpress +DB_HOST=app_db +DB_USER=wordpress +DB_PASSWORD=wordpress + +WP_TABLE_PREFIX=wp_ +WP_URL=http://localhost:8091 +WP_DOMAIN=localhost +WP_ROOT_FOLDER=/tmp/wordpress + +ADMIN_EMAIL=admin@example.test +ADMIN_USERNAME=admin +ADMIN_PASSWORD=password +ADMIN_PATH=/wp-admin + +# Used by Codeception/WPBrowser +# If running locally, these are likely the same as above +TEST_DB_NAME=wptests +TEST_DB_HOST=127.0.0.1 +TEST_DB_USER=root +TEST_DB_PASSWORD=root + +TEST_WP_TABLE_PREFIX=wp_ +TEST_WP_URL=http://localhost:8091 +TEST_WP_DOMAIN=localhost +TEST_WP_ROOT_FOLDER=/tmp/wordpress +TEST_ADMIN_EMAIL=admin@example.test + +TESTS_DIR=tests +TESTS_OUTPUT=tests/_output +TESTS_DATA=tests/_data +TESTS_SUPPORT=tests/_support +TESTS_ENVS=tests/_envs + +# Used by wp-config.php +WORDPRESS_DB_HOST=${DB_HOST} +WORDPRESS_DB_USER=${DB_USER} +WORDPRESS_DB_PASSWORD=${DB_PASSWORD} +WORDPRESS_DB_NAME=${DB_NAME} +WORDPRESS_TABLE_PREFIX=${WP_TABLE_PREFIX} + +# If your docker instance differs from the local WP install, change these values. +MYSQL_ROOT_PASSWORD=${DB_PASSWORD} +MYSQL_DATABASE=${DB_NAME} +MYSQL_USER=${DB_USER} +MYSQL_PASSWORD=${DB_PASSWORD} + +# Change these to test different versions of WP, WPGraphQL, ACF etc. +WP_VERSION=6.2 +PHP_VERSION=8.0 +WPGRAPHQL_VERSION=latest \ No newline at end of file diff --git a/.github/actions/setup-wordpress/action.yml b/.github/actions/setup-wordpress/action.yml new file mode 100644 index 00000000..2a880083 --- /dev/null +++ b/.github/actions/setup-wordpress/action.yml @@ -0,0 +1,28 @@ +name: Set up WordPress +description: Sets up WordPress. Assumes mariadb is available as a service. + +runs: + using: 'composite' + steps: + - name: Setup PHP w/ Composer & WP-CLI + uses: shivammathur/setup-php@v2 + with: + php-version: 8.0 + extensions: mbstring, intl, bcmath, exif, gd, mysqli, opcache, zip, pdo_mysql + coverage: none + tools: composer:v2, wp-cli + + - name: Setup Node.js + uses: actions/setup-node@v3 + with: + node-version: 18.x + + - name: Install dependencies + shell: bash + run: composer install --no-dev + + - name: Setup WordPress + shell: bash + run: | + cp .env.dist .env + composer run install-test-env \ No newline at end of file diff --git a/.github/workflows/e2e-tests.yml b/.github/workflows/e2e-tests.yml index cc4a8192..f835d1c2 100644 --- a/.github/workflows/e2e-tests.yml +++ b/.github/workflows/e2e-tests.yml @@ -13,7 +13,7 @@ jobs: strategy: fail-fast: false matrix: - node: ['16'] + node: ['18'] steps: - uses: actions/checkout@v3 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b627e5b7..4d6b79eb 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -13,10 +13,10 @@ jobs: - name: Checkout Repo uses: actions/checkout@v3 - - name: Setup Node.js 16.x + - name: Setup Node.js 18.x uses: actions/setup-node@v3 with: - node-version: 16.x + node-version: 18.x - name: Install Dependencies run: npm ci diff --git a/.github/workflows/schema-linter.yml b/.github/workflows/schema-linter.yml new file mode 100644 index 00000000..43e86de9 --- /dev/null +++ b/.github/workflows/schema-linter.yml @@ -0,0 +1,78 @@ +name: Schema Linter + +on: + push: + branches: + - develop + - main + pull_request: + branches: + - develop + - main + +jobs: + run: + runs-on: ubuntu-latest + name: Lint WPGraphQL Schema + if: contains(github.event.pull_request.labels.*.name, 'safe to test ✔') || github.repository == github.event.repository.full_name || github.event_name == 'push' + + services: + mariadb: + image: mariadb:10.8.2 + ports: + - 3306:3306 + env: + MYSQL_ROOT_PASSWORD: root + # Ensure docker waits for mariadb to start + options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3 + + steps: + - name: Cancel previous runs of this workflow (pull requests only) + if: ${{ github.event_name == 'pull_request_target' }} + uses: styfle/cancel-workflow-action@0.11.0 + + - name: Checkout + uses: actions/checkout@v3 + with: + ref: ${{ github.event.pull_request.head.sha }} + + - name: Setup WordPress + uses: ./.github/actions/setup-wordpress + + - name: Setup GraphQL Schema Linter + run: npm install -g graphql-schema-linter@^3.0 graphql@^16 + + - name: Generate the Static Schema + run: | + cd /tmp/wordpress/ + # Output: /tmp/schema.graphql + wp graphql generate-static-schema + + - name: Lint the Static Schema + run: | + graphql-schema-linter --except=relay-connection-types-spec,relay-page-info-spec /tmp/schema.graphql + + - name: Display ignored linting errors + run: | + graphql-schema-linter /tmp/schema.graphql || true + + - name: Get Latest tag + uses: actions-ecosystem/action-get-latest-tag@v1 + id: get-latest-tag + + - name: Test Schema for breaking changes + run: | + echo "Previous tagged schema ${{ steps.get-latest-tag.outputs.tag }}" + + - name: Get Previous Released Schema + run: curl 'https://github.com/wp-graphql-content-blocks/releases/download/${{ steps.get-latest-tag.outputs.tag }}/schema.graphql' -L --output /tmp/${{ steps.get-latest-tag.outputs.tag }}.graphql + + # https://github.com/marketplace/actions/graphql-inspector + - name: Install Schema Inspector + run: | + npm install @graphql-inspector/config @graphql-inspector/cli graphql + + - name: Run Schema Inspector + run: | + # This schema and previous release schema + node_modules/.bin/graphql-inspector diff /tmp/${{ steps.get-latest-tag.outputs.tag }}.graphql /tmp/schema.graphql \ No newline at end of file diff --git a/.github/workflows/upload-schema-artifact.yml b/.github/workflows/upload-schema-artifact.yml new file mode 100644 index 00000000..59cb9593 --- /dev/null +++ b/.github/workflows/upload-schema-artifact.yml @@ -0,0 +1,39 @@ +name: Upload Schema Artifact + +on: + release: + types: [ published ] + +jobs: + run: + runs-on: ubuntu-latest + name: Generate and Upload WPGraphQL Schema Artifact + services: + mariadb: + image: mariadb:10.8.2 + ports: + - 3306:3306 + env: + MYSQL_ROOT_PASSWORD: root + # Ensure docker waits for mariadb to start + options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3 + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + ref: ${{ github.event.pull_request.head.sha }} + - name: Setup WordPress + uses: ./.github/actions/setup-wordpress + + - name: Generate the Static Schema + run: | + cd /tmp/wordpress + # Output: /tmp/schema.graphql + wp graphql generate-static-schema + + - name: Upload schema as release artifact + uses: softprops/action-gh-release@v1 + with: + files: /tmp/schema.graphql + env: + GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} \ No newline at end of file diff --git a/bin/_env.sh b/bin/_env.sh new file mode 100644 index 00000000..9cae9363 --- /dev/null +++ b/bin/_env.sh @@ -0,0 +1,40 @@ +#!/usr/bin/env bash + +set +u + +print_usage_instruction() { + echo "ERROR!" + echo "Values in the .env file are missing or incorrect." + echo "Open the .env file at the root of this plugin and enter values to match your local environment settings" + exit 1 +} + +if [[ -z "$TEST_DB_NAME" ]]; then + echo "TEST_DB_NAME not found" + print_usage_instruction +else + DB_NAME=$TEST_DB_NAME +fi +if [[ -z "$TEST_DB_USER" ]]; then + echo "TEST_DB_USER not found" + print_usage_instruction +else + DB_USER=$TEST_DB_USER +fi + +DB_HOST=${TEST_DB_HOST-localhost} +DB_PASS=${TEST_DB_PASSWORD-""} +WP_VERSION=${WP_VERSION-latest} +TMPDIR=${TMPDIR-/tmp} +TMPDIR=$(echo $TMPDIR | sed -e "s/\/$//") +WP_TESTS_DIR=${WP_TESTS_DIR-$TMPDIR/wordpress-tests-lib} +WP_CORE_DIR=${TEST_WP_ROOT_FOLDER-$TMPDIR/wordpress/} +PLUGIN_DIR=$(pwd) +if [[ ! -z "$PLUGIN_SLUG" ]]; then + PLUGIN_DIR="${PLUGIN_DIR}/${PLUGIN_SLUG}" + echo "Using $PLUGIN_DIR as source" +fi + +WP_CLI_CONFIG_PATH="${PLUGIN_DIR}/bin/wp-cli.yml" + +SKIP_DB_CREATE=${SKIP_DB_CREATE-false} \ No newline at end of file diff --git a/bin/_lib.sh b/bin/_lib.sh new file mode 100644 index 00000000..7bd66e66 --- /dev/null +++ b/bin/_lib.sh @@ -0,0 +1,152 @@ +#!/usr/bin/env bash +set +u + +export WP_CLI_ALLOW_ROOT=1 + +download() { + if [ $(which curl) ]; then + curl -s "$1" >"$2" + elif [ $(which wget) ]; then + wget -nv -O "$2" "$1" + fi +} + +install_wordpress() { + + if [ -d $WP_CORE_DIR ]; then + return; + fi + + mkdir -p $WP_CORE_DIR + + if [[ $WP_VERSION == 'nightly' || $WP_VERSION == 'trunk' ]]; then + mkdir -p $TMPDIR/wordpress-trunk + rm -rf $TMPDIR/wordpress-trunk/* + svn export --quiet https://core.svn.wordpress.org/trunk $TMPDIR/wordpress-trunk/wordpress + mv $TMPDIR/wordpress-trunk/wordpress/* $WP_CORE_DIR + else + if [ $WP_VERSION == 'latest' ]; then + local ARCHIVE_NAME='latest' + elif [[ $WP_VERSION =~ [0-9]+\.[0-9]+ ]]; then + # https serves multiple offers, whereas http serves single. + download https://api.wordpress.org/core/version-check/1.7/ $TMPDIR/wp-latest.json + if [[ $WP_VERSION =~ [0-9]+\.[0-9]+\.[0] ]]; then + # version x.x.0 means the first release of the major version, so strip off the .0 and download version x.x + LATEST_VERSION=${WP_VERSION%??} + else + # otherwise, scan the releases and get the most up to date minor version of the major release + local VERSION_ESCAPED=`echo $WP_VERSION | sed 's/\./\\\\./g'` + LATEST_VERSION=$(grep -o '"version":"'$VERSION_ESCAPED'[^"]*' $TMPDIR/wp-latest.json | sed 's/"version":"//' | head -1) + fi + if [[ -z "$LATEST_VERSION" ]]; then + local ARCHIVE_NAME="wordpress-$WP_VERSION" + else + local ARCHIVE_NAME="wordpress-$LATEST_VERSION" + fi + else + local ARCHIVE_NAME="wordpress-$WP_VERSION" + fi + download https://wordpress.org/${ARCHIVE_NAME}.tar.gz $TMPDIR/wordpress.tar.gz + tar --strip-components=1 -zxmf $TMPDIR/wordpress.tar.gz -C $WP_CORE_DIR + fi + + download https://raw.github.com/markoheijnen/wp-mysqli/master/db.php $WP_CORE_DIR/wp-content/db.php +} + +recreate_db() { + shopt -s nocasematch + if [[ $1 =~ ^(y|yes)$ ]] + then + mysqladmin drop $DB_NAME -f --user="$DB_USER" --password="$DB_PASS"$EXTRA + create_db + echo "Recreated the database ($DB_NAME)." + else + echo "Leaving the existing database ($DB_NAME) in place." + fi + shopt -u nocasematch +} + +create_db() { + mysqladmin create $DB_NAME --user="$DB_USER" --password="$DB_PASS"$EXTRA +} + +install_db() { + + if [ ${SKIP_DB_CREATE} = "true" ]; then + return 0 + fi + + # parse DB_HOST for port or socket references + local PARTS=(${DB_HOST//\:/ }) + local DB_HOSTNAME=${PARTS[0]}; + local DB_SOCK_OR_PORT=${PARTS[1]}; + local EXTRA="" + + if ! [ -z $DB_HOSTNAME ] ; then + if [ $(echo $DB_SOCK_OR_PORT | grep -e '^[0-9]\{1,\}$') ]; then + EXTRA=" --host=$DB_HOSTNAME --port=$DB_SOCK_OR_PORT --protocol=tcp" + elif ! [ -z $DB_SOCK_OR_PORT ] ; then + EXTRA=" --socket=$DB_SOCK_OR_PORT" + elif ! [ -z $DB_HOSTNAME ] ; then + EXTRA=" --host=$DB_HOSTNAME --protocol=tcp" + fi + fi + + # create database + if [ $(mysql --user="$DB_USER" --password="$DB_PASS"$EXTRA --execute='show databases;' | grep ^$DB_NAME$) ] + then + echo "Reinstalling will delete the existing test database ($DB_NAME)" + read -p 'Are you sure you want to proceed? [y/N]: ' DELETE_EXISTING_DB + recreate_db $DELETE_EXISTING_DB + else + create_db + fi +} + +configure_wordpress() { + if [ "${SKIP_WP_SETUP}" = "true" ]; then + echo "Skipping WordPress setup..." + return 0 + fi + + cd $WP_CORE_DIR + + echo "Setting up WordPress..." + export WP_CLI_CONFIG_PATH=${WP_CLI_CONFIG_PATH}; + + wp config create --dbname="$DB_NAME" --dbuser="$DB_USER" --dbpass="$DB_PASS" --dbhost="$DB_HOST" --skip-check --force=true + wp core install --url=$WP_DOMAIN --title=Test --admin_user=$ADMIN_USERNAME --admin_password=$ADMIN_PASSWORD --admin_email=$ADMIN_EMAIL + wp rewrite structure '/%year%/%monthnum%/%postname%/' --hard +} + +setup_plugin() { + # Add this repo as a plugin to the repo + if [ ! -d $WP_CORE_DIR/wp-content/plugins/wp-graphql-content-blocks ]; then + ln -s $PLUGIN_DIR $WP_CORE_DIR/wp-content/plugins/wp-graphql-content-blocks + cd $WP_CORE_DIR/wp-content/plugins + pwd + ls + fi + + cd $WP_CORE_DIR + + wp plugin list + + # Install WPGraphQL + wp plugin install wp-graphql + + # Activate WPGraphQL + wp plugin activate wp-graphql + + # activate the plugin + wp plugin activate wp-graphql-content-blocks + + # List the active plugins + wp plugin list + + # Flush the permalinks + wp rewrite flush + + # Export the db for codeception to use + wp db export $PLUGIN_DIR/tests/_data/dump.sql +} diff --git a/bin/install-test-env.sh b/bin/install-test-env.sh new file mode 100644 index 00000000..785bb9ab --- /dev/null +++ b/bin/install-test-env.sh @@ -0,0 +1,18 @@ +#!/usr/bin/env bash + +if [[ ! -f ".env" ]]; then + echo "No .env file was detected. .env.dist has been copied to .env" + echo "Open the .env file and enter values to match your local environment" + cp .env.dist .env +fi + +source .env + +BASEDIR=$(dirname "$0"); +source ${BASEDIR}/_env.sh +source ${BASEDIR}/_lib.sh + +install_wordpress +install_db +configure_wordpress +setup_plugin \ No newline at end of file diff --git a/bin/install-wp-tests.sh b/bin/install-wp-tests.sh index ee05775c..4900affd 100755 --- a/bin/install-wp-tests.sh +++ b/bin/install-wp-tests.sh @@ -17,13 +17,8 @@ TMPDIR=$(echo $TMPDIR | sed -e "s/\/$//") WP_TESTS_DIR=${WP_TESTS_DIR-$TMPDIR/wordpress-tests-lib} WP_CORE_DIR=${WP_CORE_DIR-$TMPDIR/wordpress} -download() { - if [ `which curl` ]; then - curl -s "$1" > "$2"; - elif [ `which wget` ]; then - wget -nv -O "$2" "$1" - fi -} +BASEDIR=$(dirname "$0"); +source ${BASEDIR}/_lib.sh if [[ $WP_VERSION =~ ^[0-9]+\.[0-9]+\-(beta|RC)[0-9]+$ ]]; then WP_BRANCH=${WP_VERSION%\-*} @@ -51,49 +46,7 @@ else fi WP_TESTS_TAG="tags/$LATEST_VERSION" fi -set -ex - -install_wp() { - - if [ -d $WP_CORE_DIR ]; then - return; - fi - mkdir -p $WP_CORE_DIR - - if [[ $WP_VERSION == 'nightly' || $WP_VERSION == 'trunk' ]]; then - mkdir -p $TMPDIR/wordpress-trunk - rm -rf $TMPDIR/wordpress-trunk/* - svn export --quiet https://core.svn.wordpress.org/trunk $TMPDIR/wordpress-trunk/wordpress - mv $TMPDIR/wordpress-trunk/wordpress/* $WP_CORE_DIR - else - if [ $WP_VERSION == 'latest' ]; then - local ARCHIVE_NAME='latest' - elif [[ $WP_VERSION =~ [0-9]+\.[0-9]+ ]]; then - # https serves multiple offers, whereas http serves single. - download https://api.wordpress.org/core/version-check/1.7/ $TMPDIR/wp-latest.json - if [[ $WP_VERSION =~ [0-9]+\.[0-9]+\.[0] ]]; then - # version x.x.0 means the first release of the major version, so strip off the .0 and download version x.x - LATEST_VERSION=${WP_VERSION%??} - else - # otherwise, scan the releases and get the most up to date minor version of the major release - local VERSION_ESCAPED=`echo $WP_VERSION | sed 's/\./\\\\./g'` - LATEST_VERSION=$(grep -o '"version":"'$VERSION_ESCAPED'[^"]*' $TMPDIR/wp-latest.json | sed 's/"version":"//' | head -1) - fi - if [[ -z "$LATEST_VERSION" ]]; then - local ARCHIVE_NAME="wordpress-$WP_VERSION" - else - local ARCHIVE_NAME="wordpress-$LATEST_VERSION" - fi - else - local ARCHIVE_NAME="wordpress-$WP_VERSION" - fi - download https://wordpress.org/${ARCHIVE_NAME}.tar.gz $TMPDIR/wordpress.tar.gz - tar --strip-components=1 -zxmf $TMPDIR/wordpress.tar.gz -C $WP_CORE_DIR - fi - - download https://raw.github.com/markoheijnen/wp-mysqli/master/db.php $WP_CORE_DIR/wp-content/db.php -} install_test_suite() { # portable in-place argument for both GNU sed and Mac OSX sed @@ -126,56 +79,6 @@ install_test_suite() { } -recreate_db() { - shopt -s nocasematch - if [[ $1 =~ ^(y|yes)$ ]] - then - mysqladmin drop $DB_NAME -f --user="$DB_USER" --password="$DB_PASS"$EXTRA - create_db - echo "Recreated the database ($DB_NAME)." - else - echo "Leaving the existing database ($DB_NAME) in place." - fi - shopt -u nocasematch -} - -create_db() { - mysqladmin create $DB_NAME --user="$DB_USER" --password="$DB_PASS"$EXTRA -} - -install_db() { - - if [ ${SKIP_DB_CREATE} = "true" ]; then - return 0 - fi - - # parse DB_HOST for port or socket references - local PARTS=(${DB_HOST//\:/ }) - local DB_HOSTNAME=${PARTS[0]}; - local DB_SOCK_OR_PORT=${PARTS[1]}; - local EXTRA="" - - if ! [ -z $DB_HOSTNAME ] ; then - if [ $(echo $DB_SOCK_OR_PORT | grep -e '^[0-9]\{1,\}$') ]; then - EXTRA=" --host=$DB_HOSTNAME --port=$DB_SOCK_OR_PORT --protocol=tcp" - elif ! [ -z $DB_SOCK_OR_PORT ] ; then - EXTRA=" --socket=$DB_SOCK_OR_PORT" - elif ! [ -z $DB_HOSTNAME ] ; then - EXTRA=" --host=$DB_HOSTNAME --protocol=tcp" - fi - fi - - # create database - if [ $(mysql --user="$DB_USER" --password="$DB_PASS"$EXTRA --execute='show databases;' | grep ^$DB_NAME$) ] - then - echo "Reinstalling will delete the existing test database ($DB_NAME)" - read -p 'Are you sure you want to proceed? [y/N]: ' DELETE_EXISTING_DB - recreate_db $DELETE_EXISTING_DB - else - create_db - fi -} - -install_wp +install_wordpress install_test_suite install_db diff --git a/composer.json b/composer.json index ced996c0..971b3595 100644 --- a/composer.json +++ b/composer.json @@ -22,6 +22,7 @@ "roave/security-advisories": "dev-latest" }, "scripts": { + "install-test-env": "bash bin/install-test-env.sh", "lint": "parallel-lint -e php --no-colors --exclude vendor .", "phpcs": "phpcs", "phpcs:fix": "phpcbf", diff --git a/includes/Type/InterfaceType/EditorBlockInterface.php b/includes/Type/InterfaceType/EditorBlockInterface.php index 26f125bb..546fdd70 100644 --- a/includes/Type/InterfaceType/EditorBlockInterface.php +++ b/includes/Type/InterfaceType/EditorBlockInterface.php @@ -52,7 +52,8 @@ public static function register_type(): void { ), 'args' => array( 'flat' => array( - 'type' => 'Boolean', + 'description' => __( 'Returns the list of blocks as a flat list if true', 'wp-graphql-content-blocks' ), + 'type' => 'Boolean', ), ), 'description' => __( 'List of editor blocks', 'wp-graphql-content-blocks' ), diff --git a/includes/Type/InterfaceType/PostTypeBlockInterface.php b/includes/Type/InterfaceType/PostTypeBlockInterface.php index 14c237aa..80c72112 100644 --- a/includes/Type/InterfaceType/PostTypeBlockInterface.php +++ b/includes/Type/InterfaceType/PostTypeBlockInterface.php @@ -24,6 +24,11 @@ public static function register_type( string $post_type, array $block_names = ar register_graphql_interface_type( ucfirst( $post_type ) . 'EditorBlock', array( + 'description' => sprintf( + // translators: EditorBlock Interface for %s Block Type. + __( 'EditorBlock Interface for %s Block Type', 'wp-graphql-content-blocks' ), + ucfirst( $post_type ) + ), 'interfaces' => array( 'EditorBlock' ), 'fields' => array( 'name' => array( diff --git a/includes/Type/Scalar/Scalar.php b/includes/Type/Scalar/Scalar.php index 10372a4f..e7559547 100644 --- a/includes/Type/Scalar/Scalar.php +++ b/includes/Type/Scalar/Scalar.php @@ -18,7 +18,8 @@ public function init(): void { register_graphql_scalar( 'BlockAttributesObject', array( - 'serialize' => function ( $value ) { + 'description' => __( 'Generic Object Scalar Type', 'wp-graphql-content-blocks' ), + 'serialize' => function ( $value ) { return wp_json_encode( $value ); }, ) diff --git a/tests/_data/.gitignore b/tests/_data/.gitignore new file mode 100644 index 00000000..b503cd89 --- /dev/null +++ b/tests/_data/.gitignore @@ -0,0 +1 @@ +dump.sql \ No newline at end of file