From 1b407ddb3ff32456650679c2efc8ce30b511977d Mon Sep 17 00:00:00 2001 From: erseco Date: Sat, 11 Jul 2026 11:01:12 +0100 Subject: [PATCH 1/5] build(phpcs): anchor the uploads exclude-pattern to close the local false-green MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The bare `/exelearning/*` matched the FULL path, so any checkout under a directory containing an `exelearning/` segment (e.g. .../ate/exelearning/wp-exelearning/, or the wp-env mount .../plugins/exelearning/) excluded the ENTIRE plugin — local `make phpcs` "passed" in 61ms by scanning nothing, while CI (checkout .../wp-exelearning/) scanned normally and failed. Anchor it to the intended target only, the WordPress uploads extraction dir. Verified from the real checkout path: `phpcs -q .` now scans the plugin (3.5s, was 61ms), is clean, a probe violation under includes/ is caught, and a probe under wp-content/uploads/exelearning/ stays excluded. No other ruleset behavior changed. --- .phpcs.xml.dist | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.phpcs.xml.dist b/.phpcs.xml.dist index 5273f60..9599569 100644 --- a/.phpcs.xml.dist +++ b/.phpcs.xml.dist @@ -9,7 +9,10 @@ /tests/* /includes/vendor/* /dist/* - /exelearning/* + + */wp-content/uploads/exelearning/* /bin/* From 9d7f666cf8c8c9bb82e13f6c25453c1ca27f4993 Mon Sep 17 00:00:00 2001 From: erseco Date: Tue, 28 Jul 2026 15:42:15 +0100 Subject: [PATCH 2/5] Remove the orphan Exelearning_Admin class left by the plugin generator public/views/elp-list.php declared Exelearning_Admin, a leftover from the WordPress plugin generator. Nothing reaches it: the plugin has no autoloader (every class is pulled in by an explicit require_once from exelearning.php), no require_once names this path, and it was the only file under public/views/. Its strings, nonce and hooks are all dead with it. The coverage exclude in phpunit.xml.dist that carried it -- "requires non-existent file dependency" -- goes away with the file it was excluding. The catalogs under languages/ are regenerated because the file's strings and source references go with it: "Select eXeLearning File" and "Use this file" were unique to it, and "Settings" loses one of its three references. CI regenerates them and fails on a stale tree, so they belong in this commit. --- languages/exelearning-ca.po | 9 -- languages/exelearning-ca_valencia.po | 9 -- languages/exelearning-de_DE.po | 9 -- languages/exelearning-eo.po | 9 -- languages/exelearning-es_ES.po | 9 -- languages/exelearning-eu.po | 9 -- languages/exelearning-gl_ES.po | 9 -- languages/exelearning-it_IT.po | 9 -- languages/exelearning-pt_PT.po | 9 -- languages/exelearning-ro_RO.po | 9 -- languages/exelearning.pot | 9 -- phpunit.xml.dist | 2 - public/views/elp-list.php | 153 --------------------------- 13 files changed, 254 deletions(-) delete mode 100644 public/views/elp-list.php diff --git a/languages/exelearning-ca.po b/languages/exelearning-ca.po index 5e720dc..700cba0 100644 --- a/languages/exelearning-ca.po +++ b/languages/exelearning-ca.po @@ -43,7 +43,6 @@ msgid "https://exelearning.net/" msgstr "https://exelearning.net/" #: admin/class-admin-settings.php:72 -#: public/views/elp-list.php:63 #: assets/js/elp-upload.js:403 msgid "Settings" msgstr "Configuració" @@ -820,14 +819,6 @@ msgstr "Carrega el contingut interactiu" msgid "Download source file" msgstr "Descarrega el fitxer font" -#: public/views/elp-list.php:112 -msgid "Select eXeLearning File" -msgstr "Selecciona un fitxer eXeLearning" - -#: public/views/elp-list.php:113 -msgid "Use this file" -msgstr "Utilitza aquest fitxer" - #: assets/js/elp-upload.js:37 msgid "Web (_web.zip)" msgstr "Web (_web.zip)" diff --git a/languages/exelearning-ca_valencia.po b/languages/exelearning-ca_valencia.po index f3d8481..4a880cf 100644 --- a/languages/exelearning-ca_valencia.po +++ b/languages/exelearning-ca_valencia.po @@ -43,7 +43,6 @@ msgid "https://exelearning.net/" msgstr "https://exelearning.net/" #: admin/class-admin-settings.php:72 -#: public/views/elp-list.php:63 #: assets/js/elp-upload.js:403 msgid "Settings" msgstr "Configuració" @@ -820,14 +819,6 @@ msgstr "Carrega el contingut interactiu" msgid "Download source file" msgstr "Descarregar el fitxer font" -#: public/views/elp-list.php:112 -msgid "Select eXeLearning File" -msgstr "Seleccionar un fitxer eXeLearning" - -#: public/views/elp-list.php:113 -msgid "Use this file" -msgstr "Utilitzar este fitxer" - #: assets/js/elp-upload.js:37 msgid "Web (_web.zip)" msgstr "Web (_web.zip)" diff --git a/languages/exelearning-de_DE.po b/languages/exelearning-de_DE.po index 607224d..b63ffb2 100644 --- a/languages/exelearning-de_DE.po +++ b/languages/exelearning-de_DE.po @@ -43,7 +43,6 @@ msgid "https://exelearning.net/" msgstr "https://exelearning.net/" #: admin/class-admin-settings.php:72 -#: public/views/elp-list.php:63 #: assets/js/elp-upload.js:403 msgid "Settings" msgstr "Einstellungen" @@ -820,14 +819,6 @@ msgstr "Interaktive Inhalte laden" msgid "Download source file" msgstr "Quelldatei herunterladen" -#: public/views/elp-list.php:112 -msgid "Select eXeLearning File" -msgstr "eXeLearning-Datei auswählen" - -#: public/views/elp-list.php:113 -msgid "Use this file" -msgstr "Diese Datei verwenden" - #: assets/js/elp-upload.js:37 msgid "Web (_web.zip)" msgstr "Web (_web.zip)" diff --git a/languages/exelearning-eo.po b/languages/exelearning-eo.po index b0a19d2..e01cdf8 100644 --- a/languages/exelearning-eo.po +++ b/languages/exelearning-eo.po @@ -43,7 +43,6 @@ msgid "https://exelearning.net/" msgstr "https://exelearning.net/" #: admin/class-admin-settings.php:72 -#: public/views/elp-list.php:63 #: assets/js/elp-upload.js:403 msgid "Settings" msgstr "Agordoj" @@ -820,14 +819,6 @@ msgstr "Ŝargi interagan enhavon" msgid "Download source file" msgstr "Elŝuti fontdosieron" -#: public/views/elp-list.php:112 -msgid "Select eXeLearning File" -msgstr "Elekti eXeLearning-dosieron" - -#: public/views/elp-list.php:113 -msgid "Use this file" -msgstr "Uzi ĉi tiun dosieron" - #: assets/js/elp-upload.js:37 msgid "Web (_web.zip)" msgstr "Web (_web.zip)" diff --git a/languages/exelearning-es_ES.po b/languages/exelearning-es_ES.po index c1f6f37..d24cd6e 100644 --- a/languages/exelearning-es_ES.po +++ b/languages/exelearning-es_ES.po @@ -43,7 +43,6 @@ msgid "https://exelearning.net/" msgstr "https://exelearning.net/" #: admin/class-admin-settings.php:72 -#: public/views/elp-list.php:63 #: assets/js/elp-upload.js:403 msgid "Settings" msgstr "Ajustes" @@ -820,14 +819,6 @@ msgstr "Cargar contenido interactivo" msgid "Download source file" msgstr "Descargar archivo fuente" -#: public/views/elp-list.php:112 -msgid "Select eXeLearning File" -msgstr "Seleccionar archivo eXeLearning" - -#: public/views/elp-list.php:113 -msgid "Use this file" -msgstr "Usar este archivo" - #: assets/js/elp-upload.js:37 msgid "Web (_web.zip)" msgstr "Web (_web.zip)" diff --git a/languages/exelearning-eu.po b/languages/exelearning-eu.po index 628558b..0ce7032 100644 --- a/languages/exelearning-eu.po +++ b/languages/exelearning-eu.po @@ -43,7 +43,6 @@ msgid "https://exelearning.net/" msgstr "https://exelearning.net/" #: admin/class-admin-settings.php:72 -#: public/views/elp-list.php:63 #: assets/js/elp-upload.js:403 msgid "Settings" msgstr "Ezarpenak" @@ -820,14 +819,6 @@ msgstr "Kargatu eduki interaktiboa" msgid "Download source file" msgstr "Deskargatu iturburu fitxategia" -#: public/views/elp-list.php:112 -msgid "Select eXeLearning File" -msgstr "Hautatu eXeLearning fitxategia" - -#: public/views/elp-list.php:113 -msgid "Use this file" -msgstr "Erabili fitxategi hau" - #: assets/js/elp-upload.js:37 msgid "Web (_web.zip)" msgstr "Web (_web.zip)" diff --git a/languages/exelearning-gl_ES.po b/languages/exelearning-gl_ES.po index 1a0933c..301e87c 100644 --- a/languages/exelearning-gl_ES.po +++ b/languages/exelearning-gl_ES.po @@ -43,7 +43,6 @@ msgid "https://exelearning.net/" msgstr "https://exelearning.net/" #: admin/class-admin-settings.php:72 -#: public/views/elp-list.php:63 #: assets/js/elp-upload.js:403 msgid "Settings" msgstr "Axustes" @@ -820,14 +819,6 @@ msgstr "Cargar contido interactivo" msgid "Download source file" msgstr "Descargar ficheiro fonte" -#: public/views/elp-list.php:112 -msgid "Select eXeLearning File" -msgstr "Seleccionar ficheiro eXeLearning" - -#: public/views/elp-list.php:113 -msgid "Use this file" -msgstr "Usar este ficheiro" - #: assets/js/elp-upload.js:37 msgid "Web (_web.zip)" msgstr "Web (_web.zip)" diff --git a/languages/exelearning-it_IT.po b/languages/exelearning-it_IT.po index 9886f76..b8f8705 100644 --- a/languages/exelearning-it_IT.po +++ b/languages/exelearning-it_IT.po @@ -43,7 +43,6 @@ msgid "https://exelearning.net/" msgstr "https://exelearning.net/" #: admin/class-admin-settings.php:72 -#: public/views/elp-list.php:63 #: assets/js/elp-upload.js:403 msgid "Settings" msgstr "Impostazioni" @@ -820,14 +819,6 @@ msgstr "Carica contenuto interattivo" msgid "Download source file" msgstr "Scarica file sorgente" -#: public/views/elp-list.php:112 -msgid "Select eXeLearning File" -msgstr "Seleziona file eXeLearning" - -#: public/views/elp-list.php:113 -msgid "Use this file" -msgstr "Usa questo file" - #: assets/js/elp-upload.js:37 msgid "Web (_web.zip)" msgstr "Web (_web.zip)" diff --git a/languages/exelearning-pt_PT.po b/languages/exelearning-pt_PT.po index 21af4a4..31b4668 100644 --- a/languages/exelearning-pt_PT.po +++ b/languages/exelearning-pt_PT.po @@ -43,7 +43,6 @@ msgid "https://exelearning.net/" msgstr "https://exelearning.net/" #: admin/class-admin-settings.php:72 -#: public/views/elp-list.php:63 #: assets/js/elp-upload.js:403 msgid "Settings" msgstr "Definições" @@ -820,14 +819,6 @@ msgstr "Carregar conteúdo interativo" msgid "Download source file" msgstr "Descarregar ficheiro fonte" -#: public/views/elp-list.php:112 -msgid "Select eXeLearning File" -msgstr "Selecionar ficheiro eXeLearning" - -#: public/views/elp-list.php:113 -msgid "Use this file" -msgstr "Usar este ficheiro" - #: assets/js/elp-upload.js:37 msgid "Web (_web.zip)" msgstr "Web (_web.zip)" diff --git a/languages/exelearning-ro_RO.po b/languages/exelearning-ro_RO.po index 2dfd927..38d1483 100644 --- a/languages/exelearning-ro_RO.po +++ b/languages/exelearning-ro_RO.po @@ -43,7 +43,6 @@ msgid "https://exelearning.net/" msgstr "https://exelearning.net/" #: admin/class-admin-settings.php:72 -#: public/views/elp-list.php:63 #: assets/js/elp-upload.js:403 msgid "Settings" msgstr "Setări" @@ -824,14 +823,6 @@ msgstr "Încarcă conținut interactiv" msgid "Download source file" msgstr "Descarcă fișierul sursă" -#: public/views/elp-list.php:112 -msgid "Select eXeLearning File" -msgstr "Selectează fișierul eXeLearning" - -#: public/views/elp-list.php:113 -msgid "Use this file" -msgstr "Folosește acest fișier" - #: assets/js/elp-upload.js:37 msgid "Web (_web.zip)" msgstr "Web (_web.zip)" diff --git a/languages/exelearning.pot b/languages/exelearning.pot index 4bafdd6..f016131 100644 --- a/languages/exelearning.pot +++ b/languages/exelearning.pot @@ -42,7 +42,6 @@ msgid "https://exelearning.net/" msgstr "" #: admin/class-admin-settings.php:72 -#: public/views/elp-list.php:63 #: assets/js/elp-upload.js:403 msgid "Settings" msgstr "" @@ -819,14 +818,6 @@ msgstr "" msgid "Download source file" msgstr "" -#: public/views/elp-list.php:112 -msgid "Select eXeLearning File" -msgstr "" - -#: public/views/elp-list.php:113 -msgid "Use this file" -msgstr "" - #: assets/js/elp-upload.js:37 msgid "Web (_web.zip)" msgstr "" diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 67e2eca..1262cb2 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -17,8 +17,6 @@ admin/vendor admin/class-admin-upload.php - - public/views/elp-list.php includes/class-cli-command.php diff --git a/public/views/elp-list.php b/public/views/elp-list.php deleted file mode 100644 index 52f9fd6..0000000 --- a/public/views/elp-list.php +++ /dev/null @@ -1,153 +0,0 @@ - - */ -class Exelearning_Admin { - - /** - * The ID of this plugin. - * - * @access private - * @var string $plugin_name The ID of this plugin. - */ - private $plugin_name; - - /** - * The version of this plugin. - * - * @access private - * @var string $version The current version of this plugin. - */ - private $version; - - /** - * Initialize the class and set its properties. - * - * @param string $plugin_name The name of this plugin. - * @param string $version The version of this plugin. - */ - public function __construct( $plugin_name, $version ) { - $this->plugin_name = $plugin_name; - $this->version = $version; - - $this->load_dependencies(); - add_filter( 'plugin_action_links_' . plugin_basename( EXELEARNING_PLUGIN_FILE ), array( $this, 'add_settings_link' ) ); - add_action( 'save_post_exelearning', array( $this, 'save_post' ), 10, 1 ); - } - - /** - * Add settings link to the plugins page. - * - * @param array $links The existing links. - * @return array The modified links. - */ - public function add_settings_link( $links ) { - $settings_link = '' . __( 'Settings', 'exelearning' ) . ''; - array_unshift( $links, $settings_link ); - return $links; - } - - /** - * Load the required dependencies for this class. - * - * @since 1.0.0 - * @access private - */ - private function load_dependencies() { - require_once plugin_dir_path( __DIR__ ) . 'admin/class-exelearning-admin-settings.php'; - - if ( ! has_action( 'admin_menu', array( 'Exelearning_Admin_Settings', 'create_menu' ) ) ) { - new Exelearning_Admin_Settings(); - } - } - - - /** - * Register the stylesheets for the admin area. - * - * @param string $hook_suffix The current admin page. - */ - public function enqueue_styles( $hook_suffix ) { - if ( 'settings_page_exelearning_settings' !== $hook_suffix ) { - return; - } - wp_enqueue_style( $this->plugin_name, plugin_dir_url( __FILE__ ) . 'css/exelearning-admin.css', array(), $this->version, 'all' ); - } - - /** - * Register the JavaScript for the admin area. - * - * @param string $hook_suffix The current admin page. - */ - public function enqueue_scripts( $hook_suffix ) { - if ( ! in_array( $hook_suffix, array( 'post.php', 'post-new.php', 'settings_page_exelearning_settings', 'upload.php', 'media-new.php' ), true ) ) { - return; - } - - wp_enqueue_media(); - wp_enqueue_script( $this->plugin_name, plugin_dir_url( __FILE__ ) . 'js/exelearning-admin.js', array( 'jquery' ), $this->version, false ); - - wp_localize_script( - $this->plugin_name, - 'exelearningAdmin', - array( - 'title' => __( 'Select eXeLearning File', 'exelearning' ), - 'button' => __( 'Use this file', 'exelearning' ), - 'allowedTypes' => array( - 'application/zip', - '.elp', - ), - ) - ); - } - - /** - * Save post metadata when a post is saved. - * - * @param int $post_id The post ID. - */ - public function save_post( $post_id ) { - // Check if our nonce is set. - if ( ! isset( $_POST['exelearning_content_nonce'] ) ) { - return; - } - - // Verify that the nonce is valid. - if ( ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['exelearning_content_nonce'] ) ), 'exelearning_content_metabox' ) ) { - return; - } - - // If this is an autosave, our form has not been submitted, so we don't want to do anything. - if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) { - return; - } - - // Check the user's permissions. - if ( ! current_user_can( 'edit_post', $post_id ) ) { - return; - } - - // Save the .elp file ID. - if ( isset( $_POST['exelearning_file_id'] ) ) { - update_post_meta( $post_id, '_exelearning_file_id', sanitize_text_field( wp_unslash( $_POST['exelearning_file_id'] ) ) ); - } - } -} From 0fc2a318939cfccc3f5f1f20cd5156aa74916afd Mon Sep 17 00:00:00 2001 From: erseco Date: Tue, 28 Jul 2026 15:49:05 +0100 Subject: [PATCH 3/5] Reset the script registry between block tests WP_UnitTestCase keeps a single $wp_scripts for the whole process, so registrations and inline data accumulate across the tests in this file. Any assertion of the form "is this script on the page?" therefore passes as soon as some earlier test enqueued it, in file order, whether or not the code under test did anything -- the check stops being a check. set_up() now drops $GLOBALS['wp_scripts'] so each test starts from an empty registry and WordPress rebuilds it on first use. --- tests/unit/ElpUploadBlockTest.php | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/unit/ElpUploadBlockTest.php b/tests/unit/ElpUploadBlockTest.php index 3597023..2e355af 100644 --- a/tests/unit/ElpUploadBlockTest.php +++ b/tests/unit/ElpUploadBlockTest.php @@ -24,6 +24,7 @@ class ElpUploadBlockTest extends WP_UnitTestCase { */ public function set_up() { parent::set_up(); + $this->reset_scripts(); $this->block = new ExeLearning_Elp_Upload_Block(); } @@ -842,4 +843,16 @@ public function test_block_render_enqueues_dashicons() { $this->assertTrue( wp_style_is( 'dashicons', 'enqueued' ) ); } + + /** + * Start each test with an empty script registry. + * + * WP_UnitTestCase keeps one $wp_scripts for the whole process, so registrations and + * inline data pile up across tests: whichever test enqueues first makes every later + * assertion about "is this on the page?" true for free, in file order, whether or not + * the code under test did anything. + */ + private function reset_scripts() { + $GLOBALS['wp_scripts'] = null; + } } From 12da2d3137f5ce769c8071ef8e452bb75c883444 Mon Sep 17 00:00:00 2001 From: erseco Date: Tue, 28 Jul 2026 17:13:29 +0100 Subject: [PATCH 4/5] Reset the style registry between tests as well The helper dropped $wp_scripts but not $wp_styles, and this file asserts against both -- wp_style_is() for exelearning-frontend, the block editor sheet and dashicons. The style registry therefore kept accumulating across tests exactly as the script registry used to. Demonstrated rather than assumed: a probe test that enqueues nothing, placed after test_enqueue_frontend_styles_enqueues_style, still saw exelearning-frontend as enqueued. With $wp_styles dropped it does not. Renamed to reset_assets() since it now covers both registries. Dropping $wp_styles is safe because WordPress rebuilds it lazily on next use and re-registers the defaults, dashicons included -- confirmed by the suite, not by reading the source. --- tests/unit/ElpUploadBlockTest.php | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/tests/unit/ElpUploadBlockTest.php b/tests/unit/ElpUploadBlockTest.php index 2e355af..c730d68 100644 --- a/tests/unit/ElpUploadBlockTest.php +++ b/tests/unit/ElpUploadBlockTest.php @@ -24,7 +24,7 @@ class ElpUploadBlockTest extends WP_UnitTestCase { */ public function set_up() { parent::set_up(); - $this->reset_scripts(); + $this->reset_assets(); $this->block = new ExeLearning_Elp_Upload_Block(); } @@ -845,14 +845,19 @@ public function test_block_render_enqueues_dashicons() { } /** - * Start each test with an empty script registry. + * Start each test with empty script and style registries. * - * WP_UnitTestCase keeps one $wp_scripts for the whole process, so registrations and - * inline data pile up across tests: whichever test enqueues first makes every later - * assertion about "is this on the page?" true for free, in file order, whether or not - * the code under test did anything. + * WP_UnitTestCase keeps one $wp_scripts and one $wp_styles for the whole process, so + * registrations and inline data pile up across tests: whichever test enqueues first + * makes every later assertion about "is this on the page?" true for free, in file + * order, whether or not the code under test did anything. This file asserts against + * both registries, so both have to be dropped. + * + * WordPress rebuilds each one lazily on next use -- wp_scripts() and wp_styles() + * re-register the defaults, dashicons included -- so nothing has to be restored here. */ - private function reset_scripts() { + private function reset_assets() { $GLOBALS['wp_scripts'] = null; + $GLOBALS['wp_styles'] = null; } } From bb6e99f35d94f74ff28f0654ab105a601a72ca4c Mon Sep 17 00:00:00 2001 From: erseco Date: Wed, 29 Jul 2026 07:57:22 +0100 Subject: [PATCH 5/5] Match the uploads exclude-pattern against the relative path and guard it in CI Exclude patterns without a type attribute are matched against the absolute file path, so */wp-content/uploads/exelearning/* still excluded the entire plugin from a checkout living under a path like /srv/wp-content/uploads/exelearning/work/plugin/ - the same class of false green the pattern rewrite was meant to close, just from a rarer trigger. type="relative" matches against the path inside the scanned directory, which the checkout's own location can never satisfy. Verified both ways: a probe with violations under the repo's wp-content/uploads/exelearning/ stays excluded, and a copy of the tree nested under a wp-content/uploads/exelearning/ parent is scanned in full (34 files) where the absolute pattern scanned zero with exit 0. CI now re-runs phpcs on such a nested copy and fails if zero files are checked, so the regression cannot come back silently. The copy is made with git checkout-index because .gitattributes export-ignores dotfiles, including .phpcs.xml.dist itself. --- .github/workflows/ci.yml | 15 +++++++++++++++ .phpcs.xml.dist | 10 ++++++---- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3317f9b..dd77bb8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -116,6 +116,21 @@ jobs: - name: Run code linting (PHPCS) run: ./vendor/bin/phpcs --standard=.phpcs.xml.dist -q . + # Regression guard for the uploads exclude-pattern: scan a copy of the + # tree nested under a wp-content/uploads/exelearning/ parent directory. + # An absolute-type pattern matches that checkout location itself and + # silently excludes every file (exit 0, zero files checked), so assert + # the nested copy is actually scanned. checkout-index, not git archive: + # .gitattributes export-ignores dotfiles, including .phpcs.xml.dist. + - name: PHPCS exclude-pattern regression guard + run: | + nested="${RUNNER_TEMP}/wp-content/uploads/exelearning/plugin" + mkdir -p "${nested}" + git checkout-index --all --prefix="${nested}/" + checked=$(./vendor/bin/phpcs --standard="${nested}/.phpcs.xml.dist" --report=json -q "${nested}" | jq '.files | length') + echo "PHPCS checked ${checked} file(s) in the nested checkout" + test "${checked}" -gt 0 + # === JS UNIT TESTS (no WordPress runtime needed; fail fast before wp-env) === - name: Run JavaScript unit tests run: npm run test:js diff --git a/.phpcs.xml.dist b/.phpcs.xml.dist index 9599569..36bdee9 100644 --- a/.phpcs.xml.dist +++ b/.phpcs.xml.dist @@ -9,10 +9,12 @@ /tests/* /includes/vendor/* /dist/* - - */wp-content/uploads/exelearning/* + + ^wp-content/uploads/exelearning/* /bin/*