diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0db93d8e1cd6..656331b77e6d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -25,7 +25,7 @@ jobs: build-plugin: name: Build plugin runs-on: ubuntu-22.04 - if: startsWith( github.repository, 'elementor/' ) +# if: startsWith( github.repository, 'elementor/' ) outputs: artifact_name: ${{ env.PLUGIN_FOLDER_FILENAME}} changelog_diff: ${{ steps.changelog_diff_files.outputs.diff }} diff --git a/.github/workflows/config.json b/.github/workflows/config.json index 66d3f4149a75..0890d77741a9 100644 --- a/.github/workflows/config.json +++ b/.github/workflows/config.json @@ -2,6 +2,6 @@ "deployment" : { "repository_owner" : "elementor", "environment" : "prod", - "permitted" : "Batsirai-muchareva,TzviRabinovitch,davseve,louiswol94,mykytamurzin,ronenelementor,hein-obox,MichaelLiamin,elchugreeva" + "permitted" : "Batsirai-muchareva,TzviRabinovitch,davseve,louiswol94,mykytamurzin,ronenelementor,hein-obox,MichaelLiamin,elchugreeva,asafdl,mugurelLupu,baghdasarovelementor,mike-elementor,Svitlana-Dykun" } } diff --git a/.github/workflows/one-click-release.yml b/.github/workflows/one-click-release.yml index 08567a0cd04b..a0610974a290 100644 --- a/.github/workflows/one-click-release.yml +++ b/.github/workflows/one-click-release.yml @@ -11,6 +11,10 @@ on: - ga - beta - cloud + custom_core_executed: + type: boolean + description: 'Was the Custom Core process executed and completed successful?' + required: true pre_release: type: boolean description: 'Pre-release?' @@ -28,6 +32,11 @@ jobs: release: runs-on: ubuntu-22.04 steps: + - name: Check if Custom Core was executed + if: github.event.inputs.custom_core_executed == 'false' + run: | + echo "Custom Core was not executed. Please run Custom Core before running this workflow." + exit 1 - name: checkout branch uses: actions/checkout@v4 with: diff --git a/.github/workflows/playwright-wp-now-new.yml b/.github/workflows/playwright-wp-now-new.yml new file mode 100644 index 000000000000..ea1d49731a44 --- /dev/null +++ b/.github/workflows/playwright-wp-now-new.yml @@ -0,0 +1,203 @@ +name: Playwright wp-now new + +on: + pull_request: + paths-ignore: + - '**.md' + - '**.txt' + - '.github/config.json' + - 'bin/**' + - '.gitignore' + - 'docs/**' + workflow_dispatch: + inputs: + reporter: + required: false + description: 'Select a reporter' + type: choice + options: + - allure-playwright + - html + - blob + - list + default: allure-playwright + path-to-results: + required: false + description: 'Provide path to reporter files' + default: allure-results + type: choice + options: + - test-results/ + - tests/playwright/blob-report + - allure-results + fail_fast: + type: boolean + required: true + description: 'Cancel tests when one of them fails' + default: false + tag: + description: 'Provide @tag or a keyword' + required: false + +# This allows a subsequently queued workflow run to interrupt previous runs +concurrency: + group: '${{ github.workflow }} @ ${{ github.event.pull_request.head.label || github.head_ref || github.ref }}' + cancel-in-progress: true + +jobs: + build-plugin: + name: Build plugin + uses: ./.github/workflows/build.yml + Playwright: + name: Playwright test - ${{ matrix.shardIndex }} on PHP 8.0 + runs-on: ubuntu-latest + needs: [build-plugin] + if: ${{ ( github.event.pull_request.title == null || needs.build-plugin.outputs.changelog_diff ) && github.event.inputs.tag == '' }} + strategy: + fail-fast: ${{ inputs.fail_fast || false }} + matrix: + shardIndex: [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] + shardTotal: [ 10 ] + include: + - shardIndex: "elements-regression" + steps: + - name: Checkout source code + uses: actions/checkout@v4 + - name: Install Node.js 20.x + uses: actions/setup-node@v4 + with: + node-version: 20.x + cache: 'npm' + - name: Install dependencies + run: | + composer install + npm ci + sed -i -e "s/image: 'mariadb:lts'/image: 'mariadb:11.2.4-jammy'/g" node_modules/@wordpress/env/lib/build-docker-compose-config.js + - name: Download build artifact + uses: actions/download-artifact@v4 + with: + name: ${{ needs.build-plugin.outputs.artifact_name }} + path: ./build +# - name: Update wp-env.json file +# env: +# PHP_VERSION: '8.0' +# WP_CORE_VERSION: 'latest' +# run: node ./.github/scripts/build-wp-env.js +# - name: Install WordPress environment +# run: | +# npm run start-local-server +# - name: Update wordpress to nightly build +# if: ${{ github.event_name == 'schedule' }} +# run: npx wp-env run cli wp core update https://wordpress.org/nightly-builds/wordpress-latest.zip +# - name: Setup test data +# run: npm run test:setup:playwright +# - name: WordPress debug information +# run: | +# npx wp-env run cli wp core version +# npx wp-env run cli wp --info + - name: Install WordPress environment 8888 + run: | + npx wp-now start --php=8.0 --port=8888 --blueprint=./blueprint.json & + - name: Install WordPress environment 8889 + run: | + npx wp-now start --php=8.0 --port=8889 --blueprint=./blueprint.json & + - name: Install playwright/test + run: | + npx playwright install chromium + - name: Run Playwright tests + if: ${{ matrix.shardIndex != 'elements-regression' }} + run: npm run test:playwright -- --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }} --reporter=${{ inputs.reporter || 'list,github' }} + - name: Run element regression tests + if: ${{ matrix.shardIndex == 'elements-regression' }} + run: npm run test:playwright:elements-regression -- --reporter=${{ inputs.reporter || 'list,github' }} + - uses: actions/upload-artifact@v4 + if: always() + with: + name: playwright-test-results-${{ matrix.shardIndex }} + path: ${{ inputs.path-to-results || 'test-results/' }} + if-no-files-found: ignore + retention-days: 2 + PlaywrightWithTag: + name: Playwright test - tagged tests on PHP 8.0 + runs-on: ubuntu-latest + needs: [ build-plugin ] + if: ${{ github.event.inputs.tag }} + steps: + - name: Checkout source code + uses: actions/checkout@v4 + - name: Install Node.js 20.x + uses: actions/setup-node@v4 + with: + node-version: 20.x + cache: 'npm' + - name: Install dependencies + run: | + npm ci + sed -i -e "s/image: 'mariadb:lts'/image: 'mariadb:11.2.4-jammy'/g" node_modules/@wordpress/env/lib/build-docker-compose-config.js + - name: Download build artifact + uses: actions/download-artifact@v4 + with: + name: ${{ needs.build-plugin.outputs.artifact_name }} + path: ./build + - name: Update wp-env.json file + env: + PHP_VERSION: '8.0' + WP_CORE_VERSION: 'latest' + run: node ./.github/scripts/build-wp-env.js + - name: Install WordPress environment + run: | + npm run start-local-server + - name: Setup test data + run: npm run test:setup:playwright + - name: WordPress debug information + run: | + npx wp-env run cli wp core version + npx wp-env run cli wp --info + - name: Install playwright/test + run: | + npx playwright install chromium + - name: Run Playwright tests + run: | + npm run test:playwright -- --grep="${{ inputs.tag }}" --reporter=list,allure-playwright + - uses: actions/upload-artifact@v4 + if: always() + with: + name: playwright-test-results-tagged-tests + path: ${{ inputs.path-to-results || 'test-results/' }} + if-no-files-found: ignore + retention-days: 2 + + + test-result: + needs: Playwright + if: ${{ always() }} # Will be run even if 'Playwright' matrix will be skipped + runs-on: ubuntu-22.04 + name: Playwright - Test Results + steps: + - name: Test status + run: echo "Test status is - ${{ needs.Playwright.result }}" + - name: Checkout source code + if: ${{ needs.Playwright.result == 'failure' && github.event_name == 'schedule' }} + uses: actions/checkout@v4 +# - name: Send slack message +# if: ${{ needs.Playwright.result == 'failure' && github.event_name == 'schedule' }} +# uses: ./.github/workflows/post-to-slack +# with: +# SLACK_BOT_TOKEN: ${{ secrets.SLACK_TOKEN }} +# SLACK_TAG_CHANNELS: ${{ secrets.TEST_AUTOMATION_RESULTS }} +# PAYLOAD: | +# { +# "text": "Elementor Core: Playwright with WordPress nightly has failed: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}", +# "blocks": [ +# { +# "type": "section", +# "text": { +# "type": "mrkdwn", +# "text": "Elementor Core: Playwright with WordPress nightly failed: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" +# } +# } +# ] +# } +# - name: Check Playwright matrix status +# if: ${{ needs.Playwright.result != 'success' && needs.Playwright.result != 'skipped' }} +# run: exit 1 diff --git a/.github/workflows/pwwpnow.yml b/.github/workflows/pwwpnow.yml new file mode 100644 index 000000000000..15a79c695a4f --- /dev/null +++ b/.github/workflows/pwwpnow.yml @@ -0,0 +1,156 @@ +name: Playwright wp-now + +on: + workflow_dispatch: + inputs: + core_branch: + description: 'Elementor Core Branch' + required: true + +# This allows a subsequently queued workflow run to interrupt previous runs +concurrency: + group: '${{ github.workflow }} @ ${{ github.event.pull_request.head.label || github.head_ref || github.ref }}' + cancel-in-progress: true + +jobs: + build-plugin: + name: Build plugin + runs-on: ubuntu-latest +# if: startsWith( github.repository, 'elementor/' ) + outputs: + changelog_diff: ${{ steps.changelog_diff_files.outputs.diff }} + steps: + - name: Checkout source code + uses: actions/checkout@v3 + - name: Check if this is only a changelog PR + id: changelog_diff_files + uses: technote-space/get-diff-action@v6 + with: + # PATTERNS are: + # Everything: **/* + # Everything in directories starting with a period: .*/**/* + # Not readme.txt: !readme.txt + # Not changelog.txt: !changelog.txt + PATTERNS: | + **/* + .*/**/* + !readme.txt + !changelog.txt + - name: Install Node.js 20.x + uses: actions/setup-node@v3 + with: + node-version: 20.x + - name: Restore NPM Cache + uses: actions/cache/restore@v3 + with: + path: ~/.npm + key: npm-${{ runner.os }}-${{ hashFiles('**/package-lock.json') }} + - name: Install dependencies +# if: github.event.pull_request.title == null || steps.changelog_diff_files.outputs.diff + run: | + npm i + composer install + - name: Build +# if: github.event.pull_request.title == null || steps.changelog_diff_files.outputs.diff + run: npx grunt build + - name: Cache node modules +# if: github.event.pull_request.title == null || steps.changelog_diff_files.outputs.diff + uses: actions/cache/save@v3 + with: + path: ~/.npm + key: npm-${{ runner.os }}-${{ hashFiles('**/package-lock.json') }} + - name: Save build to cache +# if: github.event.pull_request.title == null || steps.changelog_diff_files.outputs.diff + uses: actions/cache/save@v3 + with: + path: ./build/* + key: playwright-build-${{ github.sha }} + + Playwright: + name: Playwright test - ${{ matrix.testSuite }} on PHP 8.0 + runs-on: ubuntu-latest + needs: [build-plugin] +# if: ${{ github.event.pull_request.title == null || needs.build-plugin.outputs.changelog_diff }} + strategy: + matrix: + testSuite: [ +# 'ai', +# 'onBoarding', + 'video', +# 'elements-regression', +# 'default', +# 'nested-tabs', +# 'reverse-columns', +# 'container', +# 'nested-accordion', +# 'styleguide_image_link', +# 'rating', +# 'pluginTester1_containers', +# 'pluginTester2_containers', +# 'pluginTester1_sections', +# 'pluginTester2_sections' + ] + steps: + - name: Checkout source code + uses: actions/checkout@v3 + - name: Install Node.js 20.x + uses: actions/setup-node@v3 + with: + node-version: 20.x + - name: Restore NPM from cache + uses: actions/cache/restore@v3 + id: restore-npm + with: + path: ~/.npm + key: npm-${{ runner.os }}-${{ hashFiles('**/package-lock.json') }} + - name: Restore build from cache + uses: actions/cache/restore@v3 + id: restore-build + with: + path: ./build/* + key: playwright-build-${{ github.sha }} +# - name: Install wp-cli +# run: | +# curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar +# chmod +x wp-cli.phar +# sudo mv wp-cli.phar /usr/local/bin/wp + - name: Install dependencies + run: | + composer install + npm i + - name: Install WordPress environment + run: | + npx wp-now start --php=8.0 --port=8888 --blueprint=./blueprint.json & +# - name: WP Cli check +# run: | +# wp --info +# wp plugin list +# wp theme activate hello-elementor +# - name: Run wp-now CLI commands +# run: | +# wp-now cli "wp elementor experiments activate e_optimized_css_loading" + - name: Install playwright/test + run: | + npx playwright install chromium + - name: Run Playwright tests + env: + TEST_SUITE: "@${{matrix.testSuite}}" + run: npm run test:playwright + - uses: actions/upload-artifact@v3 + if: failure() + with: + name: playwright-test-results-${{ matrix.testSuite }} + path: test-results/ + retention-days: 3 + + test-result: + needs: Playwright + if: ${{ always() }} # Will be run even if 'Playwright' matrix will be skipped + runs-on: ubuntu-22.04 + name: Playwright - Test Results + steps: + - name: Test status + run: echo "Test status is - ${{ needs.Playwright.result }}" + - name: Check Playwright matrix status + if: ${{ needs.Playwright.result != 'success' && needs.Playwright.result != 'skipped' }} + run: exit 1 diff --git a/.grunt-config/webpack.js b/.grunt-config/webpack.js index 102d75120524..147773e6f97d 100644 --- a/.grunt-config/webpack.js +++ b/.grunt-config/webpack.js @@ -192,6 +192,10 @@ const plugins = [ new WatchTimePlugin(), ]; +// Prevents the collision of chunk names between base and frontend bundles. +const baseOutputUniqueName = 'elementor'; +const frontendOutputUniqueName = 'elementorFrontend'; + const baseConfig = { target: 'web', context: __dirname, @@ -208,8 +212,6 @@ const devSharedConfig = { chunkFilename: ( chunkData ) => getChunkName( chunkData, 'development' ), filename: '[name].js', devtoolModuleFilenameTemplate: '../[resource]', - // Prevents the collision of chunk names between different bundles. - uniqueName: 'elementor', }, watch: true, }; @@ -217,6 +219,10 @@ const devSharedConfig = { const webpackConfig = [ { ...devSharedConfig, + output: { + ...devSharedConfig.output, + uniqueName: baseOutputUniqueName, + }, module: moduleRules, plugins: [ ...plugins, @@ -226,6 +232,10 @@ const webpackConfig = [ }, { ...devSharedConfig, + output: { + ...devSharedConfig.output, + uniqueName: frontendOutputUniqueName, + }, module: frontendModuleRules, plugins: [ new RemoveChunksPlugin( '.bundle.js' ), @@ -264,8 +274,6 @@ const prodSharedConfig = { path: path.resolve( __dirname, '../assets/js' ), chunkFilename: ( chunkData ) => getChunkName( chunkData, 'production' ), filename: '[name].js', - // Prevents the collision of chunk names between different bundles. - uniqueName: 'elementor', }, performance: { hints: false }, }; @@ -273,6 +281,10 @@ const prodSharedConfig = { const webpackProductionConfig = [ { ...prodSharedConfig, + output: { + ...prodSharedConfig.output, + uniqueName: baseOutputUniqueName, + }, module: moduleRules, plugins: [ ...plugins, @@ -288,6 +300,10 @@ const webpackProductionConfig = [ }, { ...prodSharedConfig, + output: { + ...prodSharedConfig.output, + uniqueName: frontendOutputUniqueName, + }, module: frontendModuleRules, plugins: [ new RemoveChunksPlugin( '.bundle.min.js' ), diff --git a/app/app.php b/app/app.php index 4a186a88fbf0..73c62ff37a7f 100644 --- a/app/app.php +++ b/app/app.php @@ -81,6 +81,8 @@ public function admin_init() { $this->enqueue_assets(); + remove_action( 'wp_print_styles', 'print_emoji_styles' ); + // Setup default heartbeat options // TODO: Enable heartbeat. add_filter( 'heartbeat_settings', function( $settings ) { diff --git a/assets/dev/js/admin/admin.js b/assets/dev/js/admin/admin.js index 3ef8624bde86..cf81f320e5db 100644 --- a/assets/dev/js/admin/admin.js +++ b/assets/dev/js/admin/admin.js @@ -135,9 +135,10 @@ import FloatingButtonsHandler from 'elementor/modules/floating-buttons/assets/js } ); $( '.e-notice--cta.e-notice--dismissible[data-notice_id="site_mailer_promotion"] a.e-button--cta' ).on( 'click', function() { + const isWcNotice = $( this ).closest( '.e-notice' ).hasClass( 'sm-notice-wc' ); elementorCommon.ajax.addRequest( 'elementor_core_site_mailer_campaign', { data: { - source: 'sm-core-form-install', + source: isWcNotice ? 'sm-core-woo-install' : 'sm-core-form-install', }, } ); } ); diff --git a/assets/dev/js/editor/components/documents/commands/close.js b/assets/dev/js/editor/components/documents/commands/close.js index 34503e1fe572..4f638c362883 100644 --- a/assets/dev/js/editor/components/documents/commands/close.js +++ b/assets/dev/js/editor/components/documents/commands/close.js @@ -47,15 +47,15 @@ export class Close extends $e.modules.CommandBase { this.confirmDialog = elementorCommon.dialogsManager.createWidget( 'confirm', { id: 'elementor-document-save-on-close', - headerMessage: __( 'Save Changes', 'elementor' ), - message: __( 'Would you like to save the changes you\'ve made?', 'elementor' ), + headerMessage: __( 'You are leaving to a separate site part.', 'elementor' ), + message: __( 'Save your changes before moving on because the current document and the one you’re moving to are separate site parts.', 'elementor' ), position: { my: 'center center', at: 'center center', }, strings: { - confirm: __( 'Save', 'elementor' ), - cancel: __( 'Discard', 'elementor' ), + confirm: __( 'Save & leave', 'elementor' ), + cancel: __( 'Don\'t leave', 'elementor' ), }, onHide: () => { // If still not action chosen. use `defer` because onHide is called before onConfirm/onCancel. @@ -65,18 +65,13 @@ export class Close extends $e.modules.CommandBase { } } ); }, + onCancel: () => { + window.top.$e.internal( 'panel/state-ready' ); + deferred.reject( 'Close document has been canceled.' ); + }, onConfirm: () => { this.args.mode = 'save'; - // Re-run with same args. - $e.run( 'editor/documents/close', this.args ) - .then( () => { - deferred.resolve(); - } ); - }, - onCancel: () => { - this.args.mode = 'discard'; - // Re-run with same args. $e.run( 'editor/documents/close', this.args ) .then( () => { diff --git a/assets/dev/js/editor/components/icons-manager/classes/icon-library.js b/assets/dev/js/editor/components/icons-manager/classes/icon-library.js index fda4cdfbce22..4719471d008a 100644 --- a/assets/dev/js/editor/components/icons-manager/classes/icon-library.js +++ b/assets/dev/js/editor/components/icons-manager/classes/icon-library.js @@ -65,12 +65,14 @@ export default class { // Enqueue CSS if ( libraryConfig.enqueue ) { libraryConfig.enqueue.forEach( ( assetURL ) => { - elementor.helpers.enqueueEditorStylesheet( assetURL ); + const versionAddedURL = `${ assetURL }${ libraryConfig?.ver ? '?ver=' + libraryConfig.ver : '' }`; + elementor.helpers.enqueueEditorStylesheet( versionAddedURL ); } ); } if ( libraryConfig.url ) { - elementor.helpers.enqueueEditorStylesheet( libraryConfig.url ); + const versionAddedURL = `${ libraryConfig.url }${ libraryConfig?.ver ? '?ver=' + libraryConfig.ver : '' }`; + elementor.helpers.enqueueEditorStylesheet( versionAddedURL ); } // Already saved an stored diff --git a/assets/dev/js/editor/controls/color.js b/assets/dev/js/editor/controls/color.js index 13dafbec37e6..82908eb5ab60 100644 --- a/assets/dev/js/editor/controls/color.js +++ b/assets/dev/js/editor/controls/color.js @@ -188,7 +188,7 @@ export default class extends ControlBaseDataView { const $color = jQuery( '
The feature `module-a` has a dependency `module-b` that is not available.
' ); // Act. update_option( @@ -566,7 +614,7 @@ public function test_validate_dependency__throws_when_a_dependency_is_inactive_a $dependant = [ 'name' => Module_A::instance()->get_name(), 'dependencies' => [ - Module_B::class, + Module_B::instance()->get_name(), ], ]; @@ -574,11 +622,11 @@ public function test_validate_dependency__throws_when_a_dependency_is_inactive_a 'name' => Module_B::instance()->get_name(), ]; - $this->add_test_feature( $dependant ); - $this->experiments->set_feature_default_state( $dependant['name'], Experiments_Manager::STATE_INACTIVE ); $this->add_test_feature( $dependency ); + $this->add_test_feature( $dependant ); $this->experiments->set_feature_default_state( $dependency['name'], Experiments_Manager::STATE_INACTIVE ); + $this->experiments->set_feature_default_state( $dependant['name'], Experiments_Manager::STATE_INACTIVE ); // Assert. $this->expectException( \WPDieException::class ); @@ -596,7 +644,7 @@ public function test_validate_dependency__deactivates_an_experiment_when_its_dep $dependant = [ 'name' => Module_A::instance()->get_name(), 'dependencies' => [ - Module_B::class, + Module_B::instance()->get_name(), ], ]; @@ -604,10 +652,10 @@ public function test_validate_dependency__deactivates_an_experiment_when_its_dep 'name' => Module_B::instance()->get_name(), ]; - $this->add_test_feature( $dependant ); - $this->experiments->set_feature_default_state( $dependant['name'], Experiments_Manager::STATE_ACTIVE ); $this->add_test_feature( $dependency ); + $this->add_test_feature( $dependant ); + $this->experiments->set_feature_default_state( $dependant['name'], Experiments_Manager::STATE_ACTIVE ); $this->experiments->set_feature_default_state( $dependency['name'], Experiments_Manager::STATE_ACTIVE ); // Act. @@ -633,7 +681,6 @@ public function test_sort_allowed_options_by_dependencies() { 'name' => 'test_B', 'dependencies' => [ 'test_A', - 'non-existing-dep-that-should-be-ignored', ], ] ); @@ -697,53 +744,6 @@ public function test_sort_allowed_options_by_dependencies() { $this->experiments->remove_feature( 'test_E' ); } - public function test_sort_allowed_options_by_dependencies__circular_deps() { - // Arrange. - $this->experiments->add_feature( [ - 'name' => 'test_A', - 'dependencies' => [ 'test_C' ], - ] ); - - $this->experiments->add_feature( [ - 'name' => 'test_B', - 'dependencies' => [ 'test_A' ], - ] ); - - $this->experiments->add_feature( [ - 'name' => 'test_C', - 'dependencies' => [ 'test_B' ], - ] ); - - // Act. - $result = apply_filters( - 'allowed_options', - [ - 'elementor' => [ - 'elementor_experiment-test_B', - 'elementor_experiment-test_C', - 'elementor_experiment-test_A', - ], - ] - ); - - // Assert. - $this->assertEqualSets( - [ - 'elementor' => [ - 'elementor_experiment-test_C', - 'elementor_experiment-test_A', - 'elementor_experiment-test_B', - ], - ], - $result - ); - - // Teardown. - $this->experiments->remove_feature( 'test_A' ); - $this->experiments->remove_feature( 'test_B' ); - $this->experiments->remove_feature( 'test_C' ); - } - private function add_test_feature( array $args = [] ) { $test_feature_data = [ 'name' => 'test_feature', @@ -811,9 +811,9 @@ public function test__feature_state_depends_on_dependency_states( $dependent_sta ] ]; - $this->add_test_feature( $dependant ); $this->add_test_feature( $dependency_a ); $this->add_test_feature( $dependency_b ); + $this->add_test_feature( $dependant ); // Act. $is_dependant_active = $this->experiments->is_feature_active( $dependant['name'], true ); diff --git a/tests/phpunit/elementor/core/experiments/test-wp-cli.php b/tests/phpunit/elementor/core/experiments/test-wp-cli.php index 1c118fc0356a..f04cd9422c68 100644 --- a/tests/phpunit/elementor/core/experiments/test-wp-cli.php +++ b/tests/phpunit/elementor/core/experiments/test-wp-cli.php @@ -24,7 +24,7 @@ public function test_activate_single_experiment() { $experiments_manager->add_feature( $test_experiment ); // Act - $wp_cli->activate( array( $experiment ), array() ); + $wp_cli->activate( [ $experiment ], [] ); $is_option_active = $experiments_manager->is_feature_active( $experiment ); // Assert @@ -49,7 +49,7 @@ public function test_activate_multiple_experiments() { $experiments_manager->add_feature( $test_experiment2 ); // Act - $wp_cli->activate( array( $experiment1 . ',' . $experiment2 ), array() ); + $wp_cli->activate( [ $experiment1 . ',' . $experiment2 ], [] ); $is_option1_active = $experiments_manager->is_feature_active( $experiment1 ); $is_option2_active = $experiments_manager->is_feature_active( $experiment2 ); @@ -70,7 +70,7 @@ public function test_deactivate_single_experiment() { $experiments_manager->add_feature( $test_experiment ); // Act - $wp_cli->deactivate( array( $experiment ), array() ); + $wp_cli->deactivate( [ $experiment ], [] ); $is_option_active = $experiments_manager->is_feature_active( $experiment ); // Assert @@ -95,7 +95,7 @@ public function test_deactivate_multiple_experiments() { $experiments_manager->add_feature( $test_experiment2 ); // Act - $wp_cli->deactivate( array( $experiment1 . ',' . $experiment2 ), array() ); + $wp_cli->deactivate( [ $experiment1 . ',' . $experiment2 ], [] ); $is_option1_active = $experiments_manager->is_feature_active( $experiment1 ); $is_option2_active = $experiments_manager->is_feature_active( $experiment2 ); diff --git a/tests/phpunit/elementor/core/settings/page/test-manager.php b/tests/phpunit/elementor/core/settings/page/test-manager.php new file mode 100644 index 000000000000..5b810653af89 --- /dev/null +++ b/tests/phpunit/elementor/core/settings/page/test-manager.php @@ -0,0 +1,149 @@ +manager = new Manager(); + } + + public function tearDown(): void { + parent::tearDown(); + $this->manager = null; + } + + public function test_ajax_before_save_settings__contributor_can_not_delete_post_thumbnail() { + // Arrange + $this->act_as('contributor'); + + $post_data = [ + 'post_author' => get_current_user_id(), + 'post_status' => 'pending', + 'post_type' => 'post', + ]; + + $document_post = $this->create_post_with_data( $post_data ); + + $data = [ + 'post_title' => 'Test Post Title', + 'post_excerpt' => 'Test Excerpt', + 'post_featured_image' => 'Test Featured Image', + ]; + + $post_id = $document_post->get_id(); + + // Assert + $this->expectExceptionCode( Exceptions::FORBIDDEN ); + $this->expectExceptionMessage( 'You do not have permission to modify the featured image.' ); + + // Act + $this->manager->ajax_before_save_settings( $data, $post_id ); + } + + public function test_save_post_status__contributor_can_not_set_post_status_private() { + // Arrange + $this->act_as('contributor'); + + $post_data = [ + 'post_author' => get_current_user_id(), + 'post_status' => 'pending', + 'post_type' => 'post', + ]; + + $document_post = $this->create_post_with_data( $post_data ); + $post_id = $document_post->get_id(); + + $data = [ + 'post_title' => 'Test Post Title', + 'post_excerpt' => 'Test Excerpt', + 'post_status' => 'private', + ]; + + // Act + $this->manager->ajax_before_save_settings( $data, $post_id ); + $document_post->refresh_post(); + $post_status = $document_post->get_post()->post_status; + + // Assert + $this->assertEquals( 'pending', $post_status ); + } + + public function test_ajax_before_save_settings__admin_can_delete_post_thumbnail() { + // Arrange + $this->act_as_admin(); + + $attachment = $this->factory()->post->create_and_get( [ + 'post_type' => 'attachment', + ] ); + $post_data = [ + 'post_author' => get_current_user_id(), + 'post_status' => 'publish', + 'post_type' => 'post', + 'meta_input' => [ + '_thumbnail_id' => $attachment->ID, + ], + ]; + $document_post = $this->create_post_with_data( $post_data ); + $data = [ + 'post_title' => 'Test Post Title', + 'post_excerpt' => 'Test Excerpt', + 'post_status' => 'publish', + 'post_featured_image' => 'Remove Featured Image', + ]; + $post_id = $document_post->get_id(); + + $this->assertEquals( get_post_thumbnail_id($post_id), $attachment->ID ); + + // Act + $this->manager->ajax_before_save_settings( $data, $post_id ); + + // Assert + $this->assertNotEquals( get_post_thumbnail_id($post_id), $attachment->ID ); + $this->assertEquals( 0, get_post_thumbnail_id($post_id)); + } + + public function test_save_post_status__admin_can_set_private_post_status() { + // Arrange + $this->act_as_admin(); + + $post_data = [ + 'post_author' => get_current_user_id(), + 'post_status' => 'pending', + 'post_type' => 'post', + ]; + + $document_post = $this->create_post_with_data( $post_data ); + $post_id = $document_post->get_id(); + + $data = [ + 'post_title' => 'Test Post Title', + 'post_excerpt' => 'Test Excerpt', + 'post_status' => 'private', + ]; + + // Act + $this->manager->ajax_before_save_settings( $data, $post_id ); + $document_post->refresh_post(); + $post_status = $document_post->get_post()->post_status; + + // Assert + $this->assertEquals( 'private', $post_status ); + } +} diff --git a/tests/phpunit/elementor/core/utils/test-plugins-manager.php b/tests/phpunit/elementor/core/utils/test-plugins-manager.php index 71eec5a6adb3..8254b94aaf3f 100644 --- a/tests/phpunit/elementor/core/utils/test-plugins-manager.php +++ b/tests/phpunit/elementor/core/utils/test-plugins-manager.php @@ -261,7 +261,7 @@ private function plugins_api_parameters_mock( $slug ) { 'plugin_information', [ 'slug' => $slug, - 'fields' => array( + 'fields' => [ 'short_description' => false, 'sections' => false, 'requires' => false, @@ -274,7 +274,7 @@ private function plugins_api_parameters_mock( $slug ) { 'compatibility' => false, 'homepage' => false, 'donate_link' => false, - ), + ], ] ]; } diff --git a/tests/phpunit/elementor/includes/sources/test-local.php b/tests/phpunit/elementor/includes/sources/test-local.php index 16aff18c40dc..a42972907177 100644 --- a/tests/phpunit/elementor/includes/sources/test-local.php +++ b/tests/phpunit/elementor/includes/sources/test-local.php @@ -30,7 +30,7 @@ public function test_maybe_render_blank_state() { // Arrange - fake globals. global $post_type, $wp_list_table; - $wp_list_table = _get_list_table( 'WP_Posts_List_Table' , array( 'screen' => 'edit-page' ) ); + $wp_list_table = _get_list_table( 'WP_Posts_List_Table' , [ 'screen' => 'edit-page' ] ); $post_type = 'post'; // Act. diff --git a/tests/phpunit/elementor/modules/atomic-widgets/styles/test-atomic-styles.php b/tests/phpunit/elementor/modules/atomic-widgets/styles/test-atomic-widget-styles.php similarity index 82% rename from tests/phpunit/elementor/modules/atomic-widgets/styles/test-atomic-styles.php rename to tests/phpunit/elementor/modules/atomic-widgets/styles/test-atomic-widget-styles.php index e2450a6eb887..43ea633f9fd8 100644 --- a/tests/phpunit/elementor/modules/atomic-widgets/styles/test-atomic-styles.php +++ b/tests/phpunit/elementor/modules/atomic-widgets/styles/test-atomic-widget-styles.php @@ -1,8 +1,10 @@ frontend = Plugin::$instance->frontend; + $this->frontend_mock = $this->createMock( Frontend::class ); + Plugin::$instance->frontend = $this->frontend_mock; + remove_all_filters( 'elementor/atomic-widgets/styles/transformers' ); remove_all_actions( 'elementor/element/parse_css' ); } + public function tear_down() { + parent::tear_down(); + + Plugin::$instance->frontend = $this->frontend; + } + public function test_parse_atomic_widget_styles__append_css_of_multiple_widgets() { // Arrange. - ( new Atomic_Styles() )->register_hooks(); + ( new Atomic_Widget_Styles() )->register_hooks(); $post = $this->make_mock_post(); $element_1 = $this->make_mock_widget([ 'controls' => [], @@ -92,7 +108,7 @@ public function test_parse_atomic_widget_styles__append_css_of_multiple_widgets( public function test_parse_atomic_widget_styles__append_css_of_styles_with_breakpoints() { // Arrange. - ( new Atomic_Styles() )->register_hooks(); + ( new Atomic_Widget_Styles() )->register_hooks(); $post = $this->make_mock_post(); $element_1 = $this->make_mock_widget([ 'controls' => [], @@ -149,7 +165,7 @@ public function test_parse_atomic_widget_styles__append_css_of_styles_with_break public function test_parse_atomic_widget_styles__append_css_of_styles_with_transformable_values() { // Arrange. - ( new Atomic_Styles() )->register_hooks(); + ( new Atomic_Widget_Styles() )->register_hooks(); $post = $this->make_mock_post(); $element = $this->make_mock_widget([ 'controls' => [], @@ -197,9 +213,43 @@ public function test_parse_atomic_widget_styles__append_css_of_styles_with_trans $this->assertMatchesSnapshot( (string) $post->get_stylesheet() ); } + public function test_parse_atomic_widget_styles__enqueue_font_family() { + // Arrange. + ( new Atomic_Widget_Styles() )->register_hooks(); + $post = $this->make_mock_post(); + $element = $this->make_mock_widget([ + 'controls' => [], + 'props_schema' => [], + 'settings' => [], + 'styles' => [ + [ + 'id' => 'test-style', + 'type' => 'class', + 'variants' => [ + [ + 'props' => [ + 'font-family' => 'Roboto', + ], + 'meta' => [], + ], + ], + ], + ], + ]); + + // Assert. + $this->frontend_mock->expects( $this->once() ) + ->method( 'enqueue_font' ) + ->with( 'Roboto' ); + + + // Act. + do_action( 'elementor/element/parse_css', $post, $element ); + } + public function test_parse_atomic_widget_styles__no_append_when_styles_are_empty() { // Arrange. - ( new Atomic_Styles() )->register_hooks(); + ( new Atomic_Widget_Styles() )->register_hooks(); $post = $this->make_mock_post(); $element = $this->make_mock_widget([ 'controls' => [], @@ -217,7 +267,7 @@ public function test_parse_atomic_widget_styles__no_append_when_styles_are_empty public function test_parse_atomic_widget_styles__invalid_non_atomic_widget() { // Arrange. - ( new Atomic_Styles() )->register_hooks(); + ( new Atomic_Widget_Styles() )->register_hooks(); $post = $this->make_mock_post(); $element = $this->mock_non_atomic_widget([ 'styles' => [ diff --git a/tests/phpunit/elementor/modules/global-classes/test-global-classes-injector.php b/tests/phpunit/elementor/modules/global-classes/test-global-classes-injector.php new file mode 100644 index 000000000000..a758877cce82 --- /dev/null +++ b/tests/phpunit/elementor/modules/global-classes/test-global-classes-injector.php @@ -0,0 +1,105 @@ + [ + 'g-4-123' => [ + 'type' => 'class', + 'id' => 'g-4-123', + 'label' => 'pinky', + 'variants' => [ + [ + 'meta' => [ + 'breakpoint' => 'mobile', + 'state' => null, + ], + 'props' => [ + 'color' => 'pink', + ], + ], + ], + ], + 'g-4-124' => [ + 'id' => 'g-4-124', + 'type' => 'class', + 'label' => 'bluey', + 'variants' => [ + [ + 'meta' => [ + 'breakpoint' => 'desktop', + 'state' => null, + ], + 'props' => [ + 'color' => 'blue', + ], + ], + ], + ], + ], + 'order' => [ 'g-4-123', 'g-4-124' ], + ]; + + private Kit $kit; + + public function setUp(): void { + parent::setUp(); + + Plugin::$instance->kits_manager->create_new_kit( 'kit' ); + $this->kit = Plugin::$instance->kits_manager->get_active_kit(); + + remove_all_actions( 'elementor/css-file/post/parse' ); + + ( new Global_Classes_Injector() )->register_hooks(); + } + + public function tearDown(): void { + parent::tearDown(); + + $this->kit->delete_meta( Global_Classes_Repository::META_KEY ); + } + + public function test_it__parses_global_classes_to_kit_css() { + // Arrange + $this->kit->update_json_meta( Global_Classes_Repository::META_KEY, $this->mock_global_classes ); + + $post = Post_CSS::create( $this->kit->get_id() ); + + // Assert + $css = $post->get_content(); + + $this->assertEquals( '@media(max-width:767px){.g-4-123{color:pink;}}.g-4-124{color:blue;}', $css ); + } + + public function test_it__does_not_parse_global_classes_to_kit_css_if_no_classes() { + // Arrange + $post = Post_CSS::create( $this->kit->get_id() ); + + // Assert + $css = $post->get_content(); + + $this->assertEquals( '', $css ); + } + + public function test_it__does_not_parse_global_classes_to_post_css_if_no_kit() { + // Arrange + $post = Post_CSS::create( uniqid() ); + + // Assert + $css = $post->get_content(); + + $this->assertEquals( '', $css ); + } +} diff --git a/tests/phpunit/elementor/modules/global-classes/test-global-classes-rest-api.php b/tests/phpunit/elementor/modules/global-classes/test-global-classes-rest-api.php new file mode 100644 index 000000000000..3a87b73cf8f7 --- /dev/null +++ b/tests/phpunit/elementor/modules/global-classes/test-global-classes-rest-api.php @@ -0,0 +1,432 @@ + "flexy", + "variants" => [ + [ + "meta" => [ + "breakpoint" => "desktop", + "state" => null + ], + 'props' => [ + 'color' => [ + '$$type' => 'color', + 'value' => 'pink', + ], + ], + ] + ] + ]; + + private $mock_global_classes = [ + 'items' => [ + 'g-4-123' => [ + 'id' => 'g-4-123', + 'label' => 'pinky', + 'variants' => [ + [ + 'meta' => [ + 'breakpoint' => 'desktop', + 'state' => null, + ], + 'props' => [ + 'color' => [ + '$$type' => 'color', + 'value' => 'pink', + ], + ], + ], + ], + ], + 'g-4-124' => [ + 'id' => 'g-4-124', + 'label' => 'bluey', + 'variants' => [ + [ + 'meta' => [ + 'breakpoint' => 'desktop', + 'state' => null, + ], + 'props' => [ + 'color' => [ + '$$type' => 'color', + 'value' => 'blue', + ], + ], + ], + ], + ], + ], + 'order' => [ 'g-4-123', 'g-4-124' ], + ]; + + public function setUp(): void { + parent::setUp(); + + global $wp_rest_server; + + $wp_rest_server = new \WP_REST_Server(); + + do_action( 'rest_api_init' ); + } + + public function tearDown(): void { + parent::tearDown(); + + Plugin::$instance->kits_manager->get_active_kit()->delete_meta( Global_Classes_Repository::META_KEY ); + } + + public function test_get__returns_all_global_classes() { + // Arrange + $this->act_as_admin(); + + Plugin::$instance->kits_manager->get_active_kit()->update_json_meta( Global_Classes_Repository::META_KEY, $this->mock_global_classes ); + + // Act + $request = new \WP_REST_Request( 'GET', '/elementor/v1/global-classes' ); + $response = rest_do_request( $request ); + + // Assert + $this->assertEquals( $this->mock_global_classes, $response->get_data() ); + $this->assertEquals( 200, $response->get_status() ); + } + + public function test_get__returns_empty_data_when_no_classes() { + // Arrange + $this->act_as_admin(); + + // Act + $request = new \WP_REST_Request( 'GET', '/elementor/v1/global-classes' ); + $response = rest_do_request( $request ); + + // Assert + $this->assertEquals( [ + 'items' => [], + 'order' => [], + ], $response->get_data() ); + $this->assertEquals( 200, $response->get_status() ); + } + + public function test_get__returns_error_when_unauthorized() { + // Arrange + $this->act_as_subscriber(); + + Plugin::$instance->kits_manager->get_active_kit()->update_json_meta( Global_Classes_Repository::META_KEY, $this->mock_global_classes ); + + // Act + $request = new \WP_REST_Request( 'GET', '/elementor/v1/global-classes' ); + $response = rest_do_request( $request ); + + // Assert + $this->assertEquals( 403, $response->get_status() ); + } + + public function test_get_by_id__returns_single_class() { + // Arrange + $this->act_as_admin(); + + Plugin::$instance->kits_manager->get_active_kit()->update_json_meta( Global_Classes_Repository::META_KEY, $this->mock_global_classes ); + + // Act + $request = new \WP_REST_Request( 'GET', '/elementor/v1/global-classes/g-4-123' ); + $response = rest_do_request( $request ); + + // Assert + $this->assertEquals( $this->mock_global_classes['items']['g-4-123'], $response->get_data() ); + $this->assertEquals( 200, $response->get_status() ); + } + + public function test_get_by_id__returns_error_when_class_not_found() { + // Arrange + $this->act_as_admin(); + + // Act + $request = new \WP_REST_Request( 'GET', '/elementor/v1/global-classes/g-4-123' ); + $response = rest_do_request( $request ); + + // Assert + $this->assertEquals( 404, $response->get_status() ); + } + + public function test_get_by_id__returns_error_when_unauthorized() { + // Arrange + $this->act_as_subscriber(); + + Plugin::$instance->kits_manager->get_active_kit()->update_json_meta( Global_Classes_Repository::META_KEY, $this->mock_global_classes ); + + // Act + $request = new \WP_REST_Request( 'GET', '/elementor/v1/global-classes/g-4-123' ); + $response = rest_do_request( $request ); + + // Assert + $this->assertEquals( 403, $response->get_status() ); + } + + public function test_delete__removes_class() { + // Arrange + $this->act_as_admin(); + + Plugin::$instance->kits_manager->get_active_kit()->update_json_meta( Global_Classes_Repository::META_KEY, $this->mock_global_classes ); + + // Act + $request = new \WP_REST_Request( 'DELETE', '/elementor/v1/global-classes/g-4-123' ); + $response = rest_do_request( $request ); + + // Assert + $classes = Plugin::$instance->kits_manager->get_active_kit()->get_json_meta( Global_Classes_Repository::META_KEY ); + + $this->assertEquals( 204, $response->get_status() ); + $this->assertArrayNotHasKey( 'g-4-123', $classes['items'] ); + } + + public function test_delete__returns_error_when_class_not_found() { + // Arrange + $this->act_as_admin(); + + // Act + $request = new \WP_REST_Request( 'DELETE', '/elementor/v1/global-classes/g-4-123' ); + $response = rest_do_request( $request ); + + // Assert + $this->assertEquals( 404, $response->get_status() ); + } + + public function test_delete__returns_error_when_unauthorized() { + // Arrange + $this->act_as_subscriber(); + + Plugin::$instance->kits_manager->get_active_kit()->update_json_meta( Global_Classes_Repository::META_KEY, $this->mock_global_classes ); + + // Act + $request = new \WP_REST_Request( 'DELETE', '/elementor/v1/global-classes/g-4-123' ); + $response = rest_do_request( $request ); + + // Assert + $this->assertEquals( 403, $response->get_status() ); + } + + public function test_put__updates_class() { + // Arrange + $this->act_as_admin(); + + Plugin::$instance->kits_manager->get_active_kit()->update_json_meta( Global_Classes_Repository::META_KEY, $this->mock_global_classes ); + + // Act + $request = new \WP_REST_Request( 'PUT', '/elementor/v1/global-classes/g-4-123' ); + $updated_class = array_merge( $this->mock_global_class, [ 'label' => 'new label' ] ); + $request->set_body_params( $updated_class ); + $response = rest_do_request( $request ); + + // Assert + $classes = Plugin::$instance->kits_manager->get_active_kit()->get_json_meta( Global_Classes_Repository::META_KEY ); + + $this->assertEquals( 200, $response->get_status() ); + $this->assertEquals( 'new label', $classes['items']['g-4-123']['label'] ); + } + public function test_put__doesnt_throw_when_data_is_identical() { + // Arrange + $this->act_as_admin(); + + Plugin::$instance->kits_manager->get_active_kit()->update_json_meta( Global_Classes_Repository::META_KEY, $this->mock_global_classes ); + + // Act + $request = new \WP_REST_Request( 'PUT', '/elementor/v1/global-classes/g-4-123' ); + $request->set_body_params( $this->mock_global_class ); + $response = rest_do_request( $request ); + + // Assert + $this->assertEquals( 200, $response->get_status() ); + } + + + public function test_put__returns_error_when_class_not_found(){ + // Arrange + $this->act_as_admin(); + + // Act + $request = new \WP_REST_Request( 'PUT', '/elementor/v1/global-classes/g-4-123' ); + $request->set_body_params( $this->mock_global_class ); + $response = rest_do_request( $request ); + + // Assert + $this->assertEquals( 404, $response->get_status() ); + } + + public function test_put__returns_error_when_data_invalid() { + // Arrange + $this->act_as_admin(); + + // Act + $request = new \WP_REST_Request( 'PUT', '/elementor/v1/global-classes/g-4-123' ); + $request->set_body_params( [] ); + $response = rest_do_request( $request ); + + // Assert + $this->assertEquals( 400, $response->get_status() ); + } + + public function test_put__returns_error_when_unauthorized() { + // Arrange + $this->act_as_subscriber(); + + Plugin::$instance->kits_manager->get_active_kit()->update_json_meta( Global_Classes_Repository::META_KEY, $this->mock_global_classes ); + + // Act + $request = new \WP_REST_Request( 'PUT', '/elementor/v1/global-classes/g-4-123' ); + $request->set_body_params( $this->mock_global_class ); + $response = rest_do_request( $request ); + + // Assert + $this->assertEquals( 403, $response->get_status() ); + } + + public function test_post__creates_new_class() { + // Arrange + $this->act_as_admin(); + + // Act + $request = new \WP_REST_Request( 'POST', '/elementor/v1/global-classes' ); + $request->set_body_params( $this->mock_global_class ); + $response = rest_do_request( $request ); + + // Assert + $classes = Plugin::$instance->kits_manager->get_active_kit()->get_json_meta( Global_Classes_Repository::META_KEY ); + $id = $response->get_data()['id']; + + $this->assertEquals( 201, $response->get_status() ); + $this->assertArrayHasKey( 'items', $classes ); + $this->assertArrayHasKey( 'order', $classes ); + $this->assertArrayHasKey( $id, $classes['items'] ); + } + + public function test_post__returns_error_when_data_invalid() { + // Arrange + $this->act_as_admin(); + + // Act + $request = new \WP_REST_Request( 'POST', '/elementor/v1/global-classes' ); + $request->set_body_params( [] ); + $response = rest_do_request( $request ); + + // Assert + $this->assertEquals( 400, $response->get_status() ); + } + + public function test_post__returns_error_when_unauthorized() { + // Arrange + $this->act_as_subscriber(); + + Plugin::$instance->kits_manager->get_active_kit()->update_json_meta( Global_Classes_Repository::META_KEY, $this->mock_global_classes ); + + // Act + $request = new \WP_REST_Request( 'POST', '/elementor/v1/global-classes' ); + $request->set_body_params( $this->mock_global_class ); + $response = rest_do_request( $request ); + + // Assert + $this->assertEquals( 403, $response->get_status() ); + } + + public function test_put_order__updates_order() { + // Arrange + $this->act_as_admin(); + + Plugin::$instance->kits_manager->get_active_kit()->update_json_meta( Global_Classes_Repository::META_KEY, $this->mock_global_classes ); + + // Act + $request = new \WP_REST_Request( 'PUT', '/elementor/v1/global-classes-order' ); + $request->set_body_params( [ 'g-4-124', 'g-4-123' ] ); + $response = rest_do_request( $request ); + + // Assert + $classes = Plugin::$instance->kits_manager->get_active_kit()->get_json_meta( Global_Classes_Repository::META_KEY ); + + $this->assertEquals( 200, $response->get_status() ); + $this->assertEquals( [ 'g-4-124', 'g-4-123' ], $classes['order'] ); + } + + public function test_put_order__doesnt_throw_when_order_is_identical(){ + // Arrange + $this->act_as_admin(); + + Plugin::$instance->kits_manager->get_active_kit()->update_json_meta( Global_Classes_Repository::META_KEY, $this->mock_global_classes ); + + // Act + $request = new \WP_REST_Request( 'PUT', '/elementor/v1/global-classes-order' ); + $request->set_body_params( $this->mock_global_classes['order'] ); + $response = rest_do_request( $request ); + + // Assert + $this->assertEquals( 200, $response->get_status() ); + } + + public function test_put_order__returns_error_when_class_not_exists_in_data(){ + // Arrange + $this->act_as_admin(); + + // Act + $request = new \WP_REST_Request( 'PUT', '/elementor/v1/global-classes-order' ); + $request->set_body_params( [ 'g-4-124' ] ); + $response = rest_do_request( $request ); + + // Assert + $this->assertEquals( 400, $response->get_status() ); + } + + public function test_put_order__returns_error_when_class_not_exists_in_updated_order(){ + // Arrange + $this->act_as_admin(); + + Plugin::$instance->kits_manager->get_active_kit()->update_json_meta( Global_Classes_Repository::META_KEY, $this->mock_global_classes ); + + // Act + $request = new \WP_REST_Request( 'PUT', '/elementor/v1/global-classes-order' ); + $request->set_body_params( [] ); + $response = rest_do_request( $request ); + + // Assert + $this->assertEquals( 400, $response->get_status() ); + } + + public function test_put_order__returns_error_when_order_does_not_match(){ + // Arrange + $this->act_as_admin(); + + Plugin::$instance->kits_manager->get_active_kit()->update_json_meta( Global_Classes_Repository::META_KEY, $this->mock_global_classes ); + + // Act + $request = new \WP_REST_Request( 'PUT', '/elementor/v1/global-classes-order' ); + $request->set_body_params( [ 'not-exist', 'g-4-124' ] ); + $response = rest_do_request( $request ); + + // Assert + $this->assertEquals( 400, $response->get_status() ); + } + + public function test_put_order__returns_error_when_unauthorized(){ + // Arrange + $this->act_as_subscriber(); + + Plugin::$instance->kits_manager->get_active_kit()->update_json_meta( Global_Classes_Repository::META_KEY, $this->mock_global_classes ); + + // Act + $request = new \WP_REST_Request( 'PUT', '/elementor/v1/global-classes-order' ); + $request->set_body_params( [ 'g-4-124', 'g-4-123' ] ); + $response = rest_do_request( $request ); + + // Assert + $this->assertEquals( 403, $response->get_status() ); + } +} diff --git a/tests/phpunit/elementor/modules/image-loading-optimization/test-module.php b/tests/phpunit/elementor/modules/image-loading-optimization/test-module.php index e482dd569e83..63e7bf2d627e 100644 --- a/tests/phpunit/elementor/modules/image-loading-optimization/test-module.php +++ b/tests/phpunit/elementor/modules/image-loading-optimization/test-module.php @@ -68,8 +68,10 @@ public function test_loading_optimization_without_logo( $page_template ) { $content = '



';
// Update the post content.
- $updated_post = array( 'ID' => $document->get_main_id(), 'post_content' => $content );
- wp_update_post( $updated_post );
+ wp_update_post( [
+ 'ID' => $document->get_main_id(),
+ 'post_content' => $content,
+ ] );
$page_templates_module = Plugin::$instance->modules_manager->get_modules( 'page-templates' );
$document->update_main_meta( '_wp_page_template', $page_template );
diff --git a/tests/phpunit/schemas/usage.json b/tests/phpunit/schemas/usage.json
index 3a896cfa1d72..a087ecdd139c 100644
--- a/tests/phpunit/schemas/usage.json
+++ b/tests/phpunit/schemas/usage.json
@@ -1314,6 +1314,12 @@
"type": "number",
"title": "Install timestamp"
},
+ "site_key": {
+ "$id": "#/site_key",
+ "type": "string",
+ "title": "Site key",
+ "description": "The site key is a unique identifier for the site."
+ },
"analytics_events": {
"$id": "#/analytics_events",
"type": "array",
diff --git a/tests/phpunit/trait-test-upgrades.php b/tests/phpunit/trait-test-upgrades.php
index 47f0363062bd..6e94a3277749 100644
--- a/tests/phpunit/trait-test-upgrades.php
+++ b/tests/phpunit/trait-test-upgrades.php
@@ -49,6 +49,26 @@ protected function create_post( $post_type = 'post' ) {
return $document;
}
+ protected function create_post_with_data( $data = [ 'post_author' => null, 'post_type' => 'post' ] ) {
+
+ if ( $data['post_author'] === null ) {
+ $admin = $this->factory()->create_and_get_administrator_user();
+
+ $data = [
+ 'post_author' => $admin->ID,
+ ];
+ }
+
+ wp_set_current_user( $data[ 'post_author' ] );
+
+ $post = $this->factory()->create_and_get_custom_post( $data );
+
+ $document = self::elementor()->documents->get( $post->ID );
+ $document->save_template_type();
+
+ return $document;
+ }
+
/**
* @param array|null $data Optional
*
diff --git a/tests/playwright/assets/api-requests.ts b/tests/playwright/assets/api-requests.ts
index cddb0eb2eac2..12f9116c9a85 100644
--- a/tests/playwright/assets/api-requests.ts
+++ b/tests/playwright/assets/api-requests.ts
@@ -1,5 +1,6 @@
import fs from 'fs';
import { type APIRequestContext } from '@playwright/test';
+
import { Image, Post, WpPage, User } from '../types/types';
export default class ApiRequests {
@@ -219,15 +220,15 @@ export default class ApiRequests {
public async deleteUser( request: APIRequestContext, userId: string ) {
const response = await request.delete( `${ this.baseUrl }/index.php`, {
- headers: {
- 'X-WP-Nonce': this.nonce,
- },
- params: {
- rest_route: `/wp/v2/users/${ userId }`,
- force: true,
- reassign: '-1',
- },
- } );
+ headers: {
+ 'X-WP-Nonce': this.nonce,
+ },
+ params: {
+ rest_route: `/wp/v2/users/${ userId }`,
+ force: true,
+ reassign: '-1',
+ },
+ } );
if ( ! response.ok() ) {
throw new Error( `
diff --git a/tests/playwright/pages/editor-page.ts b/tests/playwright/pages/editor-page.ts
index 228d34078d0c..a3b91b87f920 100644
--- a/tests/playwright/pages/editor-page.ts
+++ b/tests/playwright/pages/editor-page.ts
@@ -1,5 +1,5 @@
import { addElement, getElementSelector } from '../assets/elements-utils';
-import { expect, type Page, type Frame, type TestInfo } from '@playwright/test';
+import { expect, type Page, type Frame, type TestInfo, Locator } from '@playwright/test';
import BasePage from './base-page';
import EditorSelectors from '../selectors/editor-selectors';
import _path, { resolve as pathResolve } from 'path';
@@ -1315,4 +1315,12 @@ export default class EditorPage extends BasePage {
await this.page.locator( EditorSelectors.panels.footerTools.updateButton ).click();
await this.page.locator( EditorSelectors.toast ).waitFor();
}
+
+ async assertCorrectVwWidthStylingOfElement( element: Locator, vwValue: number = 100 ): Promise
+ Dorian Hayes
+Store Manager
+Dorian
+Hey, how can I help you today?
+ ++ Powered by Elementor
+ + Click to start chat + ]]>