From 6eac8e78ec31eb23eeaf3c64454a10acffcda009 Mon Sep 17 00:00:00 2001 From: "Soare Robert Daniel (Mac 2023)" Date: Wed, 16 Oct 2024 14:58:17 +0300 Subject: [PATCH 1/3] fix: categories backward compatibility --- classes/admin.class.php | 49 ++++++++++++++++++++++++++++------------- classes/ppom.class.php | 3 ++- 2 files changed, 36 insertions(+), 16 deletions(-) diff --git a/classes/admin.class.php b/classes/admin.class.php index 0a005cb8..a2b5da1d 100644 --- a/classes/admin.class.php +++ b/classes/admin.class.php @@ -443,7 +443,7 @@ public function get_wc_categories( $current_values ) { $used_categories = array(); if ( ! empty( $current_values['productmeta_categories'] ) ) { - $used_categories = explode( "\n", $current_values['productmeta_categories'] ); + $used_categories = preg_split('/\r\n|\n/', $current_values['productmeta_categories'] ); } foreach ( $product_categories as $category ) { @@ -620,21 +620,10 @@ function ppom_attach_ppoms() { $updated_cat = count( $categories_to_attach ); // +----- Attach Field to Tags -----+ - $categories_to_tags = isset( $_POST['ppom-attach-to-tags'] ) && is_array( $_POST['ppom-attach-to-tags'] ) ? array_map( 'sanitize_key', $_POST['ppom-attach-to-tags'] ) : array(); - $updated_tags = count( $categories_to_tags ); + $tags_to_attach = isset( $_POST['ppom-attach-to-tags'] ) && is_array( $_POST['ppom-attach-to-tags'] ) ? array_map( 'sanitize_key', $_POST['ppom-attach-to-tags'] ) : false; + $updated_tags = is_array( $tags_to_attach) ? count( $tags_to_attach ) : 0; - global $wpdb; - $ppom_table = $wpdb->prefix . PPOM_TABLE_META; - $wpdb->update( - $ppom_table, - array( - 'productmeta_categories' => implode( "\n", $categories_to_attach ), // NOTE: Keep the backward compatible format. - 'productmeta_tags' => serialize( $categories_to_tags ) - ), // Data to update - array( 'productmeta_id' => $ppom_id ), // Where clause - array( '%s' ), // Data format - array( '%d' ) // Where format - ); + self::save_categories_and_tags( $ppom_id, $categories_to_attach, $tags_to_attach ); $response = array( 'message' => "PPOM updated for {$updated_products} Products, {$updated_cat} Categories and {$updated_tags} Tags.", @@ -644,6 +633,36 @@ function ppom_attach_ppoms() { wp_send_json( $response ); } + /** + * Save the categories and tags to the given PPOM field. + * + * @param int $ppom_id The ID of the PPOM field. + * @param array $categories An array of categories to save. + * @param array|bool $tags An array of tags to save. + * + * @global wpdb $wpdb WordPress database abstraction object. + * + */ + public static function save_categories_and_tags( $ppom_id, $categories, $tags ) { + global $wpdb; + $ppom_table = $wpdb->prefix . PPOM_TABLE_META; + + $data_to_update = array( + 'productmeta_categories' => implode( "\r\n", $categories ), // NOTE: Keep the backward compatible format. + ); + + if ( is_array( $tags ) ) { + $data_to_update['productmeta_tags'] = serialize( $tags ); + } + + $wpdb->update( + $ppom_table, + $data_to_update, + array( 'productmeta_id' => $ppom_id ), // Where clause + array( '%s' ), // Data format + array( '%d' ) // Where format + ); + } /* * Plugin Validation diff --git a/classes/ppom.class.php b/classes/ppom.class.php index ced0503a..cfe6783f 100644 --- a/classes/ppom.class.php +++ b/classes/ppom.class.php @@ -374,7 +374,8 @@ function ppom_has_category_meta($product_id ) { $meta_found[] = $row->productmeta_id; } else { // making array of meta cats - $meta_cat_array = explode( "\r\n", $row->productmeta_categories ); + + $meta_cat_array = preg_split('/\r\n|\n/', $row->productmeta_categories); // Now iterating the product_categories to check it's slug in meta cats foreach ( $product_categories as $cat ) { if ( in_array( $cat->slug, $meta_cat_array ) ) { From 2f4868c4f6cae757f1fce41c087593a7756e4809 Mon Sep 17 00:00:00 2001 From: "Soare Robert Daniel (Mac 2023)" Date: Wed, 16 Oct 2024 16:40:28 +0300 Subject: [PATCH 2/3] chore: add E2E and unit test for categories saving --- .gitignore | 3 +- bin/env/create-products.sh | 28 ++++++++++---- package.json | 2 +- tests/bootstrap.php | 3 ++ tests/e2e/specs/attach-modal.spec.js | 48 ++++++++++++++++++++++- tests/e2e/specs/group-field-edit.spec.js | 7 ++++ tests/test-field-saving.php | 49 ++++++++++++++++++++++++ 7 files changed, 129 insertions(+), 11 deletions(-) create mode 100644 tests/test-field-saving.php diff --git a/.gitignore b/.gitignore index e2142e40..b4eb628d 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,5 @@ dist vendor languages/woocommerce-product-addon.pot *.log -artifacts \ No newline at end of file +artifacts +.phpunit.result.cache \ No newline at end of file diff --git a/bin/env/create-products.sh b/bin/env/create-products.sh index 2f1616cf..ab494a78 100644 --- a/bin/env/create-products.sh +++ b/bin/env/create-products.sh @@ -1,13 +1,25 @@ +# Create new categories +# NOTE: If the categories with the same slug are already present, it will raise an error. +echo "Created Category 1 with ID: $category1_id" +category1_id=$(wp wc product_cat create --name="Category 1" --slug="test_cat_1" --user=admin --porcelain) + +echo "Created Category 2 with ID: $category2_id" +category2_id=$(wp wc product_cat create --name="Category 2" --slug="test_cat_2" --user=admin --porcelain) + +echo "Created Category 3 with ID: $category3_id" +category3_id=$(wp wc product_cat create --name="Category 3" --slug="test_cat_3" --user=admin --porcelain) + # Create products and collect their IDs product1_id=$(wp wc product create --name="Product 1" --type="simple" --regular_price="9.99" --user=admin --porcelain) +echo "Created Product 1 with ID: $product1_id" + product2_id=$(wp wc product create --name="Product 2" --type="simple" --regular_price="19.99" --user=admin --porcelain) -product3_id=$(wp wc product create --name="Product 3" --type="simple" --regular_price="29.99" --user=admin --porcelain) +echo "Created Product 2 with ID: $product2_id" -# Get the first category. -category_id=$(wp wc product_cat list --user=admin --format=ids | awk '{print $1}') -echo "Category ID: $category_id" +product3_id=$(wp wc product create --name="Product 3" --type="simple" --regular_price="29.99" --user=admin --porcelain) +echo "Created Product 3 with ID: $product3_id" -# Add products to the first category -wp wc product update $product1_id --user=admin --categories='[{"id": '$category_id'}]' -wp wc product update $product2_id --user=admin --categories='[{"id": '$category_id'}]' -wp wc product update $product3_id --user=admin --categories='[{"id": '$category_id'}]' \ No newline at end of file +# Add products to the new categories +wp wc product update $product1_id --user=admin --categories='[{"id": '$category1_id'}]' +wp wc product update $product2_id --user=admin --categories='[{"id": '$category2_id'}]' +wp wc product update $product3_id --user=admin --categories='[{"id": '$category3_id'}]' \ No newline at end of file diff --git a/package.json b/package.json index bdd859c2..cd3902ee 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,6 @@ "test:e2e:debug": "wp-scripts test-playwright --config tests/e2e/playwright.config.js --ui", "test:unit:php:setup": "wp-env start", "test:unit:php:setup:debug": "wp-env start --xdebug", - "test:unit:php": "wp-env run --env-cwd='wp-content/plugins/woocommerce-product-addon' tests-wordpress vendor/bin/phpunit -c phpunit.xml.dist --verbose" + "test:unit:php": "wp-env run --env-cwd='wp-content/plugins/woocommerce-product-addon' tests-wordpress vendor/bin/phpunit -c phpunit.xml --verbose" } } diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 9c250074..32cccf86 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -24,6 +24,9 @@ */ function _register_module() { require_once dirname( dirname( __FILE__ ) ) . '/woocommerce-product-addon.php'; + include_once PPOM_PATH . '/classes/admin.class.php'; + + $ppom_admin = new NM_PersonalizedProduct_Admin(); } tests_add_filter( 'muplugins_loaded', '_register_module' ); diff --git a/tests/e2e/specs/attach-modal.spec.js b/tests/e2e/specs/attach-modal.spec.js index e5764dc3..274ca67f 100644 --- a/tests/e2e/specs/attach-modal.spec.js +++ b/tests/e2e/specs/attach-modal.spec.js @@ -5,7 +5,10 @@ import { test, expect } from "@wordpress/e2e-test-utils-playwright"; import { createSimpleGroupField } from "../utils"; test.describe("Attach Modal", () => { - test("attach to products", async ({ page, admin }) => { + /** + * Attach a new group field to the first product in list then check if it is rendered. + */ + test("attach to products and check", async ({ page, admin }) => { await createSimpleGroupField(admin, page); await page.waitForTimeout(500); await admin.visitAdminPage("admin.php?page=ppom"); @@ -36,4 +39,47 @@ test.describe("Attach Modal", () => { await expect(elements.nth(i)).toBeVisible(); } }); + + /** + * Attach a new group to multiple categories then check. + */ + test("attach to multiple categories", async ({ page, admin }) => { + const categoriesToUse = ["test_cat_1", "test_cat_2"]; + + await createSimpleGroupField(admin, page); + await page.waitForTimeout(500); + await admin.visitAdminPage("admin.php?page=ppom"); + + const firstRow = page + .locator("#ppom-groups-export-form tbody tr") + .first(); + const ppomId = await firstRow.locator("td").nth(1).innerText(); + await firstRow.getByText("Attach to Products").click(); + await page.waitForLoadState("networkidle"); + + await page.evaluate(() => { + document.querySelector('#attach-to-categories > div.postbox').classList.remove('closed'); + }); + + const categoriesSelector = page.locator( + '#ppom-product-modal select[name="ppom-attach-to-categories\\[\\]"]', + ); + + // NOTE: categories created by `create-prodcuts.sh` + await categoriesSelector.selectOption( + categoriesToUse.map((c) => ({ value: c })), + ); + await page.getByRole("button", { name: "Save" }).click(); + await page.waitForLoadState("networkidle"); + await page.reload(); + + for (const cat of categoriesToUse) { + await page.goto(`/?product_cat=${cat}`); + await page.locator('a.add_to_cart_button').first().click(); + + const elements = page.locator(`.ppom-id-${ppomId}`); + const count = await elements.count(); + expect(count ).toBeGreaterThan(0); + } + }); }); diff --git a/tests/e2e/specs/group-field-edit.spec.js b/tests/e2e/specs/group-field-edit.spec.js index 09936249..4efd514a 100644 --- a/tests/e2e/specs/group-field-edit.spec.js +++ b/tests/e2e/specs/group-field-edit.spec.js @@ -17,6 +17,10 @@ import { } from "../utils"; test.describe("Group Fields Edit", () => { + + /** + * Create two input fields and change their order then save and check. + */ test("change fields order on saving", async ({ page, admin }) => { await createSimpleGroupField(admin, page); await page.waitForTimeout(500); @@ -55,6 +59,9 @@ test.describe("Group Fields Edit", () => { expect(newOrderFieldIds).not.toEqual(fieldIds); }); + /** + * Create a select input with two option. Save then change their order and check again. + */ test("change select option order on saving", async ({ page, admin }) => { await admin.visitAdminPage("admin.php?page=ppom"); diff --git a/tests/test-field-saving.php b/tests/test-field-saving.php new file mode 100644 index 00000000..bc3d6061 --- /dev/null +++ b/tests/test-field-saving.php @@ -0,0 +1,49 @@ +prefix . PPOM_TABLE_META; + $data = array( + 'productmeta_name' => 'Test Multiple Categories', + 'productmeta_validation' => '', + 'dynamic_price_display' => 'no', + 'send_file_attachment' => '', + 'show_cart_thumb' => '', + 'aviary_api_key' => '', + 'productmeta_style' => 'selector { }', + 'productmeta_js' => '', + 'productmeta_categories' => "accessories\r\nclothing", + 'the_meta' => '{"1":{"type":"text","title":"Test Cat","data_name":"test_cat","description":"","placeholder":"","error_message":"","maxlength":"","minlength":"","default_value":"","price":"","class":"","input_mask":"","width":"12","visibility":"everyone","visibility_role":"","conditions":{"visibility":"Show","bound":"All","rules":[{"elements":"test_cat","operators":"is","element_values":""}]},"status":"on","ppom_id":"63"}}', + 'productmeta_created' => '2024-10-16 11:38:45', + 'productmeta_tags' => 'a:1:{i:0;s:8:"test-tag";}' + ); + + // Insert data into the table + $result = $wpdb->insert($table_name, $data); + $this->assertNotFalse( $result ); + + $field_id = $wpdb->insert_id; + + NM_PersonalizedProduct_Admin::save_categories_and_tags( $field_id, ['accessories', 'clothing', 'test-cat'], false ); + + $saved_data = $wpdb->get_row( $wpdb->prepare( "SELECT productmeta_categories, productmeta_tags FROM $table_name WHERE productmeta_id = %d", $field_id ), ARRAY_A ); + + $expected_categories = "accessories\r\nclothing\r\ntest-cat"; + $this->assertEquals( $expected_categories, $saved_data['productmeta_categories'] ); + + // Tags should not be changed. + $expected_tags = 'a:1:{i:0;s:8:"test-tag";}'; + $this->assertEquals( $expected_tags, $saved_data['productmeta_tags'] ); + } +} From 3603bdfeb4e3575c8a2d6d24dfe49b47d137b8ce Mon Sep 17 00:00:00 2001 From: "Soare Robert Daniel (Mac 2023)" Date: Wed, 16 Oct 2024 16:53:21 +0300 Subject: [PATCH 3/3] chore: switch to docker version --- .github/workflows/test-php.yml | 39 ++++++++++++++++------------------ 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/.github/workflows/test-php.yml b/.github/workflows/test-php.yml index 1de639cc..33f7c2ad 100644 --- a/.github/workflows/test-php.yml +++ b/.github/workflows/test-php.yml @@ -9,27 +9,24 @@ jobs: phpunit: name: Phpunit runs-on: ubuntu-22.04 - services: - mysql: - image: mysql:5.7 - env: - MYSQL_ROOT_PASSWORD: root - ports: - - 3306/tcp - options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3 steps: - - name: Setup PHP version - uses: shivammathur/setup-php@v2 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: - php-version: "7.2" - extensions: simplexml, mysql - tools: phpunit-polyfills - - name: Checkout source code - uses: actions/checkout@v2 - - name: Install WordPress Test Suite + node-version: "18" + cache: "yarn" + - name: Install NPM deps + run: | + yarn install --frozen-lockfile + - name: Install composer deps + run: composer install + - name: Install environment run: | - bash bin/install-wp-tests.sh wordpress_test root root 127.0.0.1:${{ job.services.mysql.ports['3306'] }} - - name: Install composer - run: composer install --prefer-dist --no-progress --no-suggest - - name: Run phpunit - run: phpunit + npm run wp-env start + - name: Prepare Database + run: bash ./bin/e2e-after-setup.sh + - name: Run the tests + run: | + npm run test:unit:php + env: + GITHUB_TOKEN: ${{ secrets.BOT_TOKEN }}