diff --git a/.editorconfig b/.editorconfig index d2a6a08..178a650 100644 --- a/.editorconfig +++ b/.editorconfig @@ -3,6 +3,7 @@ root = true [*] indent_style = space indent_size = 4 + end_of_line = lf charset = utf-8 trim_trailing_whitespace = true @@ -14,8 +15,11 @@ indent_size = 2 [*.md] trim_trailing_whitespace = false +[*.sh] +indent_style = tab + [*.{yaml,yml}] trim_trailing_whitespace = false -[package.json] +[{webpack.config.js,.eslintrc.js}] indent_size = 2 diff --git a/.gitattributes b/.gitattributes index 8346d85..53f8a7c 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,14 +1,18 @@ -/etc export-ignore -/features export-ignore -/spec export-ignore -/tests export-ignore -/.editorconfig export-ignore -/.gitattributes export-ignore -/.gitignore export-ignore -/.scrutinizer.yml export-ignore -/.travis.yml export-ignore -/behat.yml.dist export-ignore -/easy-coding-standard.yaml export-ignore -/phpspec.yml.dist export-ignore -/phpstan.neon export-ignore -/README.md export-ignore +/.github export-ignore +/etc export-ignore +/docs export-ignore +/.editorconfig export-ignore +/.gitattributes export-ignore +/.gitignore export-ignore +/README.md export-ignore +/behat.yml.dist export-ignore +/composer-require-checker.json export-ignore +/docs export-ignore +/ecs.php export-ignore +/node_modules export-ignore +/phpunit.xml.dist export-ignore +/psalm-baseline.xml export-ignore +/psalm.xml export-ignore +/tests export-ignore +/CHANGELOG.md export-ignore +/UPGRADE.md export-ignore diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..f2188b9 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +github: Setono diff --git a/.github/ISSUE_TEMPLATE/bug.yaml b/.github/ISSUE_TEMPLATE/bug.yaml new file mode 100644 index 0000000..3dc2acd --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug.yaml @@ -0,0 +1,36 @@ +name: Bug Report +description: File a bug report +title: "[Bug]: " +labels: [ "bug" ] +body: + - type: markdown + attributes: + value: Thanks for taking the time to fill out this bug report! + - type: input + id: plugin-version + attributes: + label: Plugin version + description: What version of the plugin are you using? + validations: + required: true + - type: input + id: sylius-version + attributes: + label: Sylius version + description: What version of Sylius are you using? + validations: + required: true + - type: textarea + id: what-happened + attributes: + label: What happened? + description: Also tell us, what did you expect to happen? + placeholder: Tell us what you see! + validations: + required: true + - type: textarea + id: logs + attributes: + label: Relevant log output + description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks. + render: shell diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..ecdc4ca --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,8 @@ +blank_issues_enabled: false +contact_links: + - name: Support question? + url: https://sylius-devs.slack.com/archives/C3EGDG9LY + about: Please ask questions on the Sylius support channel. It's okay to tag a developer, but please understand that we also have other obligations ;) + - name: Security issue + url: https://setono.com + about: Please send security vulnerabilities directly to security@setono.com diff --git a/.github/ISSUE_TEMPLATE/feature.yaml b/.github/ISSUE_TEMPLATE/feature.yaml new file mode 100644 index 0000000..cf106ab --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature.yaml @@ -0,0 +1,29 @@ +name: Feature request +description: Write a feature request +title: "[Feature request]: " +labels: [ "enhancement" ] +body: + - type: markdown + attributes: + value: "We really value feature requests. Thank you :tada:" + - type: textarea + id: what-happened + attributes: + label: Describe the proposed solution + description: A clear and concise description of what you want to happen + validations: + required: true + - type: textarea + id: alternatives + attributes: + label: Describe alternatives you've considered + description: A clear and concise description of any alternative solutions or features you've considered + validations: + required: false + - type: textarea + id: context + attributes: + label: Additional context + description: Add any other context or screenshots about the feature request here + validations: + required: false diff --git a/.github/dependabot.yml b/.github/dependabot.yml deleted file mode 100644 index dae9d88..0000000 --- a/.github/dependabot.yml +++ /dev/null @@ -1,14 +0,0 @@ -# https://docs.github.com/en/github/administering-a-repository/configuration-options-for-dependency-updates - -version: 2 - -updates: - - commit-message: - include: "scope" - prefix: "composer" - directory: "/" - open-pull-requests-limit: 1 - package-ecosystem: "composer" - schedule: - interval: "daily" - versioning-strategy: "widen" diff --git a/.github/workflows/backwards-compatibility-check.yaml b/.github/workflows/backwards-compatibility-check.yaml new file mode 100644 index 0000000..806dd6a --- /dev/null +++ b/.github/workflows/backwards-compatibility-check.yaml @@ -0,0 +1,30 @@ +# See https://github.com/Roave/BackwardCompatibilityCheck + +name: "Backwards Compatibility Check" + +on: + pull_request: ~ + +jobs: + backwards-compatibility-check: + name: "Backwards Compatibility Check" + + runs-on: "ubuntu-latest" + + steps: + - name: "Checkout" + uses: "actions/checkout@v4" + with: + fetch-depth: 0 + + - name: "Setup PHP, with composer and extensions" + uses: "shivammathur/setup-php@v2" + with: + php-version: "8.1" + coverage: "none" + + - name: "Install tool" + run: "composer global require roave/backward-compatibility-check" + + - name: "Check for BC breaks" + run: "~/.composer/vendor/bin/roave-backward-compatibility-check --from=origin/${{ github.event.pull_request.base.ref }} --format=github-actions" diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 198564f..7d7206e 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -2,36 +2,38 @@ name: "build" on: push: branches: + - "1.*.x" # Matches our branch versioning that mirrors Sylius', e.g. '1.12.x' - "master" + - "main" paths-ignore: - - "*.md" + - "**/*.md" pull_request: paths-ignore: - - "*.md" + - "**/*.md" workflow_dispatch: ~ env: APP_ENV: "test" - DATABASE_URL: "mysql://root:root@127.0.0.1/sylius?serverVersion=5.7" + DATABASE_URL: "mysql://root:root@127.0.0.1/sylius?serverVersion=8.0" PHP_EXTENSIONS: "intl, mbstring" jobs: coding-standards: - name: "Coding Standards" + name: "Coding Standards (PHP${{ matrix.php-version }} | Deps: ${{ matrix.dependencies }})" runs-on: "ubuntu-latest" strategy: matrix: php-version: - - "7.4" + - "8.1" # Always use the lowest version of PHP since a higher version could create actual syntax errors in lower versions dependencies: - "highest" steps: - name: "Checkout" - uses: "actions/checkout@v2" + uses: "actions/checkout@v4" - name: "Setup PHP, with composer and extensions" uses: "shivammathur/setup-php@v2" @@ -41,7 +43,7 @@ jobs: coverage: "none" - name: "Install composer dependencies" - uses: "ramsey/composer-install@v1" + uses: "ramsey/composer-install@v2" with: dependency-versions: "${{ matrix.dependencies }}" @@ -54,6 +56,9 @@ jobs: - name: "Check style" run: "composer check-style" + - name: "Rector" + run: "vendor/bin/rector process --dry-run" + - name: "Lint yaml files" run: "(cd tests/Application && bin/console lint:yaml ../../src/Resources)" @@ -61,21 +66,27 @@ jobs: run: "(cd tests/Application && bin/console lint:twig ../../src/Resources)" dependency-analysis: - name: "Dependency Analysis" + name: "Dependency Analysis (PHP${{ matrix.php-version }} | Deps: ${{ matrix.dependencies }} | SF${{ matrix.symfony }})" runs-on: "ubuntu-latest" strategy: matrix: php-version: - - "7.4" + - "8.1" + - "8.2" dependencies: + - "lowest" - "highest" + + symfony: + - "~5.4.0" + - "~6.4.0" steps: - name: "Checkout" - uses: "actions/checkout@v2" + uses: "actions/checkout@v4" - name: "Setup PHP, with composer and extensions" uses: "shivammathur/setup-php@v2" @@ -83,83 +94,103 @@ jobs: coverage: "none" extensions: "${{ env.PHP_EXTENSIONS }}" php-version: "${{ matrix.php-version }}" - tools: "composer-require-checker, composer-unused" + tools: "composer-require-checker, composer-unused, flex" + - name: "Remove require-dev section in composer.json" + run: "composer config --unset require-dev" + - name: "Install composer dependencies" - uses: "ramsey/composer-install@v1" + uses: "ramsey/composer-install@v2" + env: + SYMFONY_REQUIRE: "${{ matrix.symfony }}" with: dependency-versions: "${{ matrix.dependencies }}" - name: "Run maglnet/composer-require-checker" - run: "composer-require-checker check --config-file=$(pwd)/composer-require-checker.json" + run: "composer-require-checker check" - name: "Run composer-unused/composer-unused" run: "composer-unused" - + static-code-analysis: - name: "Static Code Analysis" + name: "Static Code Analysis (PHP${{ matrix.php-version }} | Deps: ${{ matrix.dependencies }} | SF${{ matrix.symfony }})" runs-on: "ubuntu-latest" strategy: matrix: php-version: - - "7.4" + - "8.1" + - "8.2" dependencies: + - "lowest" - "highest" + symfony: + - "~5.4.0" + - "~6.4.0" + steps: - name: "Checkout" - uses: "actions/checkout@v2" + uses: "actions/checkout@v4" - name: "Setup PHP, with composer and extensions" uses: "shivammathur/setup-php@v2" with: - php-version: "${{ matrix.php-version }}" - extensions: "${{ env.PHP_EXTENSIONS }}" coverage: "none" + extensions: "${{ env.PHP_EXTENSIONS }}" + php-version: "${{ matrix.php-version }}" + tools: "flex" + - name: "Remove sylius/sylius from composer.json" + run: "composer remove --dev --no-update --no-plugins --no-scripts sylius/sylius" + - name: "Install composer dependencies" - uses: "ramsey/composer-install@v1" + uses: "ramsey/composer-install@v2" + env: + SYMFONY_REQUIRE: "${{ matrix.symfony }}" with: dependency-versions: "${{ matrix.dependencies }}" - - name: "Cache warmup" - run: "(cd tests/Application && bin/console cache:warmup)" - - name: "Static analysis" - run: "composer analyse" - env: - APP_DEBUG: 1 + run: "vendor/bin/psalm --php-version=${{ matrix.php-version }}" unit-tests: - name: "Unit tests" + name: "Unit tests (PHP${{ matrix.php-version }} | Deps: ${{ matrix.dependencies }} | SF${{ matrix.symfony }})" runs-on: "ubuntu-latest" strategy: matrix: php-version: - - "7.4" - + - "8.1" + - "8.2" + dependencies: - "lowest" - "highest" + symfony: + - "~5.4.0" + - "~6.4.0" + steps: - name: "Checkout" - uses: "actions/checkout@v2" + uses: "actions/checkout@v4" - name: "Setup PHP, with composer and extensions" uses: "shivammathur/setup-php@v2" with: - php-version: "${{ matrix.php-version }}" - extensions: "${{ env.PHP_EXTENSIONS }}" coverage: "none" + extensions: "${{ env.PHP_EXTENSIONS }}" + php-version: "${{ matrix.php-version }}" + tools: "flex" - name: "Install composer dependencies" - uses: "ramsey/composer-install@v1" + uses: "ramsey/composer-install@v2" + env: + SYMFONY_REQUIRE: "${{ matrix.symfony }}" with: dependency-versions: "${{ matrix.dependencies }}" @@ -167,128 +198,108 @@ jobs: run: "composer phpunit" integration-tests: - name: "Integration tests" + name: "Integration tests (PHP${{ matrix.php-version }} | Deps: ${{ matrix.dependencies }} | SF${{ matrix.symfony }})" runs-on: "ubuntu-latest" strategy: matrix: php-version: - - "7.4" + - "8.1" + - "8.2" dependencies: + - "lowest" - "highest" + + symfony: + - "~5.4.0" + - "~6.4.0" steps: - name: "Start MySQL" run: "sudo /etc/init.d/mysql start" - name: "Checkout" - uses: "actions/checkout@v2" + uses: "actions/checkout@v4" - name: "Setup PHP, with composer and extensions" uses: "shivammathur/setup-php@v2" with: - php-version: "${{ matrix.php-version }}" - extensions: "${{ env.PHP_EXTENSIONS }}" coverage: "none" - tools: "symfony" - - - name: "Setup node" - uses: "actions/setup-node@v2" - with: - node-version: "10.x" + extensions: "${{ env.PHP_EXTENSIONS }}" + php-version: "${{ matrix.php-version }}" + tools: "flex" - name: "Install composer dependencies" - uses: "ramsey/composer-install@v1" + uses: "ramsey/composer-install@v2" + env: + SYMFONY_REQUIRE: "${{ matrix.symfony }}" with: dependency-versions: "${{ matrix.dependencies }}" - name: "Lint container" - run: "(cd tests/Application && bin/console lint:container) || true" + run: "(cd tests/Application && bin/console lint:container)" - name: "Create database" - run: "(cd tests/Application && bin/console doctrine:database:create -vvv)" + run: "(cd tests/Application && bin/console doctrine:database:create)" - name: "Create database schema" - run: "(cd tests/Application && bin/console doctrine:schema:create -vvv)" + run: "(cd tests/Application && bin/console doctrine:schema:create)" - name: "Validate Doctrine mapping" - run: "(cd tests/Application && bin/console doctrine:schema:validate -vvv)" - - - name: "Get Yarn cache directory" - id: "yarn-cache" - run: "echo \"::set-output name=dir::$(yarn cache dir)\"" + run: "(cd tests/Application && bin/console doctrine:schema:validate)" + + mutation-tests: + name: "Mutation tests" + + runs-on: "ubuntu-latest" + + strategy: + matrix: + php-version: + - "8.2" + + dependencies: + - "highest" + + steps: + - name: "Checkout" + uses: "actions/checkout@v4" - - name: "Cache Yarn" - uses: "actions/cache@v2" + - name: "Setup PHP, with composer and extensions" + uses: "shivammathur/setup-php@v2" with: - path: "${{ steps.yarn-cache.outputs.dir }}" - key: "yarn-${{ hashFiles('**/package.json **/yarn.lock') }}" - restore-keys: "yarn-" - - - name: "Install JS dependencies" - run: "(cd tests/Application && yarn install)" - - - name: "Install assets" - run: "(cd tests/Application && bin/console assets:install public -vvv)" - - - name: "Build assets" - run: "(cd tests/Application && yarn build)" - - - name: "Output PHP version for Symfony CLI" - run: "php -v | head -n 1 | awk '{ print $2 }' > .php-version" - - - name: "Install certificates" - run: "symfony server:ca:install" - - - name: "Run Chrome Headless" - run: "google-chrome-stable --enable-automation --disable-background-networking --no-default-browser-check --no-first-run --disable-popup-blocking --disable-default-apps --allow-insecure-localhost --disable-translate --disable-extensions --no-sandbox --enable-features=Metal --headless --remote-debugging-port=9222 --window-size=2880,1800 --proxy-server='direct://' --proxy-bypass-list='*' http://127.0.0.1 > /dev/null 2>&1 &" - - - name: "Wait for Chrome to start" - run: | - until curl -s http://127.0.0.1:9222/json/version | grep "Browser" > /dev/null 2>&1 - do - sleep 1 - done - - - name: "Run webserver" - run: "(cd tests/Application && symfony server:start --port=8080 --dir=public --daemon)" - - - name: "Wait for webserver to start" - run: | - until symfony server:list | grep /public > /dev/null 2>&1 - do - sleep 1 - done - - - name: "Run behat" - run: "vendor/bin/behat --colors --strict -vvv --no-interaction || vendor/bin/behat --colors --strict -vvv --no-interaction --rerun" - - - name: "Upload Behat logs" - uses: "actions/upload-artifact@v2" - if: "failure()" + coverage: "pcov" + extensions: "${{ env.PHP_EXTENSIONS }}" + php-version: "${{ matrix.php-version }}" + + - name: "Install composer dependencies" + uses: "ramsey/composer-install@v2" with: - name: "Behat logs" - path: "etc/build/" - if-no-files-found: "ignore" + dependency-versions: "${{ matrix.dependencies }}" + - name: "Run infection" + run: "vendor/bin/infection" + env: + STRYKER_DASHBOARD_API_KEY: "${{ secrets.STRYKER_DASHBOARD_API_KEY }}" + code-coverage: - name: "Code Coverage" + name: "Code Coverage (PHP${{ matrix.php-version }} | Deps: ${{ matrix.dependencies }})" runs-on: "ubuntu-latest" strategy: matrix: php-version: - - "7.4" + - "8.2" dependencies: - "highest" steps: - name: "Checkout" - uses: "actions/checkout@v2" + uses: "actions/checkout@v4" - name: "Setup PHP, with composer and extensions" uses: "shivammathur/setup-php@v2" @@ -301,7 +312,7 @@ jobs: run: "echo \"::add-matcher::${{ runner.tool_cache }}/phpunit.json\"" - name: "Install composer dependencies" - uses: "ramsey/composer-install@v1" + uses: "ramsey/composer-install@v2" with: dependency-versions: "${{ matrix.dependencies }}" diff --git a/LICENSE b/LICENSE index 3f4cf03..ddee45d 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2021 Setono +Copyright (c) 2024 Setono Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/UPGRADE.md b/UPGRADE.md deleted file mode 100644 index c1812ef..0000000 --- a/UPGRADE.md +++ /dev/null @@ -1,79 +0,0 @@ -# UPGRADE NOTES - -## From 0.1 to 0.2 - -1. Update database schema - - ```bash - bin/console doctrine:migrations:diff - bin/console doctrine:migrations:migrate - ``` - - And make sure you have something like this in it: - - ``` - ALTER TABLE setono_sylius_catalog_promotion__promotion CHANGE discount discount NUMERIC(10, 5) DEFAULT '0' NOT NULL; - ``` - -2. Change rules configuration at your `catalog_promotion` fixtures: - - - **Taxon** - - Replace: - - ``` - rules: - - type: "has_taxon" - configuration: - - "caps" - ``` - - to: - - ``` - rules: - - type: "has_taxon" - configuration: - taxons: # <--- - - "caps" - ``` - - - **Product** - - Replace: - - ``` - rules: - - type: "contains_product" - configuration: "santa-cap" - ``` - - to: - - ``` - rules: - - type: "contains_product" - configuration: - product: "santa-cap" # <--- - ``` - - - **Products** - - Replace: - - ``` - rules: - - type: "contains_products" - configuration: - - "santa-cap" - ``` - - to: - - ``` - rules: - - type: "contains_products" - configuration: - products: # <--- - - "santa-cap" - ``` diff --git a/composer-require-checker.json b/composer-require-checker.json index 5627941..781d424 100644 --- a/composer-require-checker.json +++ b/composer-require-checker.json @@ -16,26 +16,11 @@ "true", "void", "Faker\\Factory", - "Sylius\\Bundle\\CoreBundle\\Application\\SyliusPluginTrait", - "Sylius\\Bundle\\ChannelBundle\\Form\\Type\\ChannelChoiceType", - "Sylius\\Bundle\\CoreBundle\\Application\\SyliusPluginTrait", - "Sylius\\Bundle\\CoreBundle\\Fixture\\AbstractResourceFixture", - "Sylius\\Bundle\\CoreBundle\\Fixture\\Factory\\AbstractExampleFactory", - "Sylius\\Bundle\\CoreBundle\\Fixture\\OptionsResolver\\LazyOption", - "Sylius\\Bundle\\ProductBundle\\Form\\Type\\ProductAutocompleteChoiceType", - "Sylius\\Bundle\\PromotionBundle\\Form\\Type\\Core\\AbstractConfigurationCollectionType", - "Sylius\\Bundle\\TaxonomyBundle\\Form\\Type\\TaxonAutocompleteChoiceType", - "Sylius\\Bundle\\UiBundle\\Menu\\Event\\MenuBuilderEvent", - "Sylius\\Component\\Channel\\Model\\ChannelInterface", - "Sylius\\Component\\Channel\\Model\\ChannelsAwareInterface", - "Sylius\\Component\\Channel\\Repository\\ChannelRepositoryInterface", - "Sylius\\Component\\Core\\Formatter\\StringInflector", - "Sylius\\Component\\Core\\Model\\ChannelInterface", - "Sylius\\Component\\Core\\Model\\ChannelPricingInterface", - "Sylius\\Component\\Core\\Model\\ProductTaxon", - "Sylius\\Component\\Core\\Model\\ProductVariant", - "Sylius\\Component\\Core\\Repository\\ProductRepositoryInterface", - "Sylius\\Component\\Core\\Repository\\ProductVariantRepositoryInterface", - "Sylius\\Component\\Taxonomy\\Repository\\TaxonRepositoryInterface" + "Faker\\Generator", + "Setono\\JobStatusBundle\\Entity\\JobInterface", + "Setono\\JobStatusBundle\\Entity\\Spec\\LastJobWithType", + "Setono\\JobStatusBundle\\Factory\\JobFactoryInterface", + "Setono\\JobStatusBundle\\Manager\\JobManagerInterface", + "Setono\\JobStatusBundle\\Repository\\JobRepositoryInterface" ] } diff --git a/composer.json b/composer.json index 4c510a1..111513e 100644 --- a/composer.json +++ b/composer.json @@ -1,7 +1,8 @@ { "name": "setono/sylius-catalog-promotion-plugin", - "type": "sylius-plugin", "description": "Catalog promotion plugin for Sylius", + "license": "MIT", + "type": "sylius-plugin", "keywords": [ "sylius", "sylius-plugin", @@ -9,53 +10,49 @@ "setono", "promotions" ], - "license": "MIT", "require": { - "php": ">=7.4", + "php": ">=8.1", "beberlei/doctrineextensions": "^1.2", "doctrine/collections": "^1.6", "doctrine/dbal": "^2.13", "doctrine/event-manager": "^1.1", "doctrine/orm": "^2.7", "knplabs/knp-menu": "^3.2", - "setono/job-status-bundle": "^0.2.3", + "sylius/channel": "^1.0", + "sylius/channel-bundle": "^1.0", + "sylius/core": "^1.0", + "sylius/core-bundle": "^1.0", + "sylius/product-bundle": "^1.0", + "sylius/promotion": "^1.11", + "sylius/promotion-bundle": "^1.0", "sylius/registry": "^1.6", "sylius/resource-bundle": "^1.6", - "symfony/config": "^5.4 || ^6.0", - "symfony/console": "^5.4 || ^6.0", - "symfony/dependency-injection": "^5.4 || ^6.0", - "symfony/form": "^5.4 || ^6.0", - "symfony/lock": "^5.4 || ^6.0", - "symfony/options-resolver": "^5.4 || ^6.0", - "symfony/validator": "^5.4 || ^6.0", - "webmozart/assert": "^1.10" + "sylius/taxonomy": "^1.0", + "sylius/taxonomy-bundle": "^1.0", + "sylius/ui-bundle": "^1.0", + "symfony/config": "^5.4 || ^6.4", + "symfony/console": "^5.4 || ^6.4", + "symfony/dependency-injection": "^5.4 || ^6.4", + "symfony/form": "^5.4 || ^6.4", + "symfony/lock": "^5.4 || ^6.4", + "symfony/mailer": "^5.4 || ^6.4", + "symfony/options-resolver": "^5.4 || ^6.4", + "symfony/validator": "^5.4 || ^6.4", + "webmozart/assert": "^1.11" }, "require-dev": { - "fzaninotto/faker": "^1.9", - "phpspec/phpspec": "^7.1", + "fakerphp/faker": "^1.9", "phpunit/phpunit": "^9.5", - "psalm/plugin-phpunit": "^0.16.1", - "psalm/plugin-symfony": "^3.0", - "setono/code-quality-pack": "^2.1.2", + "psalm/plugin-phpunit": "^0.18.1", + "setono/code-quality-pack": "^2.6", "setono/sylius-behat-pack": "^0.2.1", - "sylius/sylius": "~1.7.0", - "symfony/debug-bundle": "^5.4 || ^6.0", - "symfony/dotenv": "^5.4 || ^6.0", - "symfony/intl": "^5.4 || ^6.0", - "symfony/web-profiler-bundle": "^5.4 || ^6.0", - "weirdan/doctrine-psalm-plugin": "^1.0" - }, - "config": { - "sort-packages": true - }, - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - }, - "unused": [ - "beberlei/doctrineextensions" - ] + "sylius/sylius": "~1.12.13", + "symfony/debug-bundle": "^5.4 || ^6.4", + "symfony/dotenv": "^5.4 || ^6.4", + "symfony/intl": "^5.4 || ^6.4", + "symfony/web-profiler-bundle": "^5.4 || ^6.4" }, + "prefer-stable": true, "autoload": { "psr-4": { "Setono\\SyliusCatalogPromotionPlugin\\": "src/" @@ -69,16 +66,25 @@ "tests/Application/Kernel.php" ] }, - "prefer-stable": true, + "config": { + "allow-plugins": { + "dealerdirect/phpcodesniffer-composer-installer": false, + "ergebnis/composer-normalize": true, + "symfony/thanks": false + }, + "sort-packages": true + }, + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, "scripts": { "all": [ "@checks", "@tests" ], - "analyse": [ - "@ensure-test-container-exists", - "psalm --threads=8" - ], + "analyse": "psalm", "assets": [ "@ensure-assets-installed", "@ensure-assets-compiled" @@ -102,7 +108,6 @@ "ensure-assets-installed": "[[ -d tests/Application/node_modules ]] || (cd tests/Application && yarn install)", "ensure-database-created": "(cd tests/Application && bin/console doctrine:database:create --if-not-exists)", "ensure-schema-updated": "(cd tests/Application && bin/console doctrine:schema:update --force)", - "ensure-test-container-exists": "[[ -f tests/Application/var/cache/test/ApplicationTests_Setono_SyliusCatalogPromotionPlugin_Application_KernelTestDebugContainer.xml ]] || tests/Application/bin/console cache:warmup --env=test", "ensure-vendors-installed": "[[ -f vendor/autoload.php ]] || COMPOSER_MEMORY_LIMIT=-1 composer install", "fix-style": "ecs check --fix", "fixtures": [ diff --git a/ecs.php b/ecs.php index 695339f..6e01c25 100644 --- a/ecs.php +++ b/ecs.php @@ -2,16 +2,16 @@ declare(strict_types=1); -use PhpCsFixer\Fixer\Operator\BinaryOperatorSpacesFixer; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -use Symplify\EasyCodingStandard\ValueObject\Option; +use Symplify\EasyCodingStandard\Config\ECSConfig; -return static function (ContainerConfigurator $containerConfigurator): void { - $containerConfigurator->import('vendor/sylius-labs/coding-standard/ecs.php'); - $containerConfigurator->parameters()->set(Option::PATHS, [ - 'src', 'tests' +return static function (ECSConfig $config): void { + $config->import('vendor/sylius-labs/coding-standard/ecs.php'); + $config->paths([ + 'src', + 'tests', ]); - $containerConfigurator->parameters()->set(Option::SKIP, [ - 'tests/Application/**', + $config->skip([ + 'tests/Application/node_modules/**', + 'tests/Application/var/**', ]); }; diff --git a/phpspec.yml.dist b/phpspec.yml.dist deleted file mode 100644 index cf4ab98..0000000 --- a/phpspec.yml.dist +++ /dev/null @@ -1,4 +0,0 @@ -suites: - main: - namespace: Setono\SyliusCatalogPromotionPlugin - psr4_prefix: Setono\SyliusCatalogPromotionPlugin diff --git a/phpunit.xml.dist b/phpunit.xml.dist index c9e5274..951de12 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,7 +1,7 @@ + bootstrap="tests/Application/config/bootstrap.php"> src/ diff --git a/psalm-baseline.xml b/psalm-baseline.xml deleted file mode 100644 index 62aef59..0000000 --- a/psalm-baseline.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - (float) $this->discount - - - diff --git a/psalm.xml b/psalm.xml index 9678ad5..3e41f91 100644 --- a/psalm.xml +++ b/psalm.xml @@ -1,33 +1,39 @@ - + - + - - tests/Application/var/cache/test/ApplicationTests_Setono_SyliusCatalogPromotionPlugin_Application_KernelTestDebugContainer.xml - - - - - + + + + + + + + + + diff --git a/rector.php b/rector.php new file mode 100644 index 0000000..af10a30 --- /dev/null +++ b/rector.php @@ -0,0 +1,25 @@ +cacheClass(FileCacheStorage::class); + $rectorConfig->cacheDirectory('./.build/rector'); + + $rectorConfig->paths([ + __DIR__ . '/src', + __DIR__ . '/tests', + ]); + + $rectorConfig->skip([ + __DIR__ . '/tests/Application', + ]); + + $rectorConfig->sets([ + LevelSetList::UP_TO_PHP_81 + ]); +}; diff --git a/spec/EventListener/UpdateManuallyDiscountedPropertySubscriberSpec.php b/spec/EventListener/UpdateManuallyDiscountedPropertySubscriberSpec.php deleted file mode 100644 index 3ea9b19..0000000 --- a/spec/EventListener/UpdateManuallyDiscountedPropertySubscriberSpec.php +++ /dev/null @@ -1,107 +0,0 @@ -shouldHaveType(UpdateManuallyDiscountedPropertySubscriber::class); - } - - public function it_implements_event_subscriber_interface(): void - { - $this->shouldImplement(EventSubscriber::class); - } - - public function it_updates_property_when_price_has_changed( - ChannelPricingInterface $channelPricing, - EntityManagerInterface $entityManager - ): void { - $changeSet = ['price' => true]; - $event = new PreUpdateEventArgs($channelPricing->getWrappedObject(), $entityManager->getWrappedObject(), $changeSet); - - $channelPricing->hasDiscount()->willReturn(true); - $channelPricing->resetBulkIdentifier()->shouldBeCalled(); - $channelPricing->setManuallyDiscounted(true)->shouldBeCalled(); - - $this->preUpdate($event); - } - - public function it_updates_property_when_original_price_has_changed( - ChannelPricingInterface $channelPricing, - EntityManagerInterface $entityManager - ): void { - $changeSet = ['originalPrice' => true]; - $event = new PreUpdateEventArgs($channelPricing->getWrappedObject(), $entityManager->getWrappedObject(), $changeSet); - - $channelPricing->hasDiscount()->willReturn(true); - $channelPricing->resetBulkIdentifier()->shouldBeCalled(); - $channelPricing->setManuallyDiscounted(true)->shouldBeCalled(); - - $this->preUpdate($event); - } - - public function it_sets_property_to_false_when_prices_are_equal_although_properties_changed( - ChannelPricingInterface $channelPricing, - EntityManagerInterface $entityManager - ): void { - $changeSet = ['originalPrice' => true]; - $event = new PreUpdateEventArgs($channelPricing->getWrappedObject(), $entityManager->getWrappedObject(), $changeSet); - - $channelPricing->hasDiscount()->willReturn(false); - $channelPricing->resetBulkIdentifier()->shouldBeCalled(); - $channelPricing->setManuallyDiscounted(false)->shouldBeCalled(); - - $this->preUpdate($event); - } - - public function it_does_not_update_property_when_no_properties_has_changed( - ChannelPricingInterface $channelPricing, - EntityManagerInterface $entityManager - ): void { - $changeSet = []; - $event = new PreUpdateEventArgs($channelPricing->getWrappedObject(), $entityManager->getWrappedObject(), $changeSet); - - $channelPricing->setManuallyDiscounted(Argument::any())->shouldNotBeCalled(); - - $this->preUpdate($event); - } - - public function it_sets_property_to_false_when_prices_are_equal( - ChannelPricingInterface $channelPricing, - EntityManagerInterface $entityManager - ): void { - $event = new LifecycleEventArgs($channelPricing->getWrappedObject(), $entityManager->getWrappedObject()); - - $channelPricing->hasDiscount()->willReturn(false); - $channelPricing->resetBulkIdentifier()->shouldBeCalled(); - $channelPricing->setManuallyDiscounted(false)->shouldBeCalled(); - - $this->prePersist($event); - } - - public function it_sets_property_to_true_when_prices_are_not_equal( - ChannelPricingInterface $channelPricing, - EntityManagerInterface $entityManager - ): void { - $event = new LifecycleEventArgs($channelPricing->getWrappedObject(), $entityManager->getWrappedObject()); - - $channelPricing->hasDiscount()->willReturn(true); - $channelPricing->resetBulkIdentifier()->shouldBeCalled(); - $channelPricing->setManuallyDiscounted(true)->shouldBeCalled(); - - $this->prePersist($event); - } -} diff --git a/src/Command/ProcessPromotionsCommand.php b/src/Command/ProcessPromotionsCommand.php index 5e04e56..7e12d7c 100644 --- a/src/Command/ProcessPromotionsCommand.php +++ b/src/Command/ProcessPromotionsCommand.php @@ -64,7 +64,7 @@ public function __construct( ProductVariantRepositoryInterface $productVariantRepository, PromotionRepositoryInterface $promotionRepository, ServiceRegistryInterface $ruleRegistry, - int $jobTtl + int $jobTtl, ) { parent::__construct(); @@ -111,7 +111,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int if (!$force && !$this->isProcessingAllowed($promotionIds, $lastJob)) { $output->writeln( 'Nothing to process at the moment. Run command with --force option to force process', - OutputInterface::VERBOSITY_VERBOSE + OutputInterface::VERBOSITY_VERBOSE, ); return 0; @@ -177,7 +177,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $startTime, $bulkIdentifier, $promotion->isExclusive(), - $promotion->isManuallyDiscountedProductsExcluded() + $promotion->isManuallyDiscountedProductsExcluded(), ); ++$i; diff --git a/src/DependencyInjection/Compiler/RegisterRulesPass.php b/src/DependencyInjection/Compiler/RegisterRulesPass.php index 098010b..917de2c 100644 --- a/src/DependencyInjection/Compiler/RegisterRulesPass.php +++ b/src/DependencyInjection/Compiler/RegisterRulesPass.php @@ -18,10 +18,6 @@ public function process(ContainerBuilder $container): void $formRegistry = $container->getDefinition('setono_sylius_catalog_promotion.form_registry.rule'); $formToLabelMap = []; - /** - * @var string $id - * @var array $tagged - */ foreach ($container->findTaggedServiceIds('setono_sylius_catalog_promotion.rule') as $id => $tagged) { /** @var array $attributes */ foreach ($tagged as $attributes) { diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php index 7a76922..04911fb 100644 --- a/src/DependencyInjection/Configuration.php +++ b/src/DependencyInjection/Configuration.php @@ -21,12 +21,12 @@ final class Configuration implements ConfigurationInterface public function getConfigTreeBuilder(): TreeBuilder { $treeBuilder = new TreeBuilder('setono_sylius_catalog_promotion'); + + /** @var ArrayNodeDefinition $rootNode */ $rootNode = $treeBuilder->getRootNode(); /** - * @psalm-suppress MixedMethodCall - * @psalm-suppress PossiblyUndefinedMethod - * @psalm-suppress PossiblyNullReference + * @psalm-suppress MixedMethodCall,PossiblyUndefinedMethod,PossiblyNullReference */ $rootNode ->addDefaultsIfNotSet() @@ -43,9 +43,7 @@ public function getConfigTreeBuilder(): TreeBuilder private function addResourcesSection(ArrayNodeDefinition $node): void { /** - * @psalm-suppress MixedMethodCall - * @psalm-suppress PossiblyUndefinedMethod - * @psalm-suppress PossiblyNullReference + * @psalm-suppress MixedMethodCall,PossiblyUndefinedMethod,PossiblyNullReference,UndefinedInterfaceMethod */ $node ->children() @@ -80,13 +78,6 @@ private function addResourcesSection(ArrayNodeDefinition $node): void ->scalarNode('repository')->cannotBeEmpty()->end() ->scalarNode('factory')->defaultValue(Factory::class)->end() ->scalarNode('form')->defaultValue(PromotionRuleType::class)->cannotBeEmpty()->end() - ->end() - ->end() - ->end() - ->end() - ->end() - ->end() - ->end() ; } } diff --git a/src/DependencyInjection/SetonoSyliusCatalogPromotionExtension.php b/src/DependencyInjection/SetonoSyliusCatalogPromotionExtension.php index 4cd4193..1aa61e7 100644 --- a/src/DependencyInjection/SetonoSyliusCatalogPromotionExtension.php +++ b/src/DependencyInjection/SetonoSyliusCatalogPromotionExtension.php @@ -16,6 +16,7 @@ public function load(array $configs, ContainerBuilder $container): void { /** * @var array{driver: string, resources: array} $config + * * @psalm-suppress PossiblyNullArgument */ $config = $this->processConfiguration($this->getConfiguration([], $container), $configs); diff --git a/src/Doctrine/ORM/ChannelPricingRepositoryTrait.php b/src/Doctrine/ORM/ChannelPricingRepositoryTrait.php index f535bd5..9b606c9 100644 --- a/src/Doctrine/ORM/ChannelPricingRepositoryTrait.php +++ b/src/Doctrine/ORM/ChannelPricingRepositoryTrait.php @@ -34,8 +34,8 @@ public function resetMultiplier(DateTimeInterface $dateTime, string $bulkIdentif ->andWhere( $qb->expr()->orX( 'o.bulkIdentifier != :bulkIdentifier', - 'o.bulkIdentifier is null' - ) + 'o.bulkIdentifier is null', + ), ) ->andWhere( $qb->expr()->orX( @@ -47,8 +47,8 @@ public function resetMultiplier(DateTimeInterface $dateTime, string $bulkIdentif 'o.bulkIdentifier is not null', // if the applied promotions is not null we know that it was discounted before, and we reset it - 'o.appliedPromotions is not null' - ) + 'o.appliedPromotions is not null', + ), ) ->setParameter('bulkIdentifier', $bulkIdentifier) ->setMaxResults(100) @@ -91,7 +91,7 @@ public function updateMultiplier( DateTimeInterface $dateTime, string $bulkIdentifier, bool $exclusive = false, - bool $manuallyDiscountedProductsExcluded = true + bool $manuallyDiscountedProductsExcluded = true, ): void { \assert($this instanceof EntityRepository); @@ -118,7 +118,7 @@ public function updateMultiplier( $qb->expr()->andX( 'channelPricing.appliedPromotions NOT LIKE :promotionEnding', 'channelPricing.appliedPromotions NOT LIKE :promotionMiddle', - ) + ), )) ->set('channelPricing.updatedAt', ':date') ->set('channelPricing.bulkIdentifier', ':bulkIdentifier') diff --git a/src/EventListener/AddTimestampableIndicesSubscriber.php b/src/EventListener/AddTimestampableIndicesSubscriber.php index 9f0bf25..f2faa50 100644 --- a/src/EventListener/AddTimestampableIndicesSubscriber.php +++ b/src/EventListener/AddTimestampableIndicesSubscriber.php @@ -53,7 +53,7 @@ private static function addIndices(ClassMetadata $metadata): void throw new RuntimeException(sprintf( 'The class %s must implement the interface, %s', $metadata->name, - TimestampableInterface::class + TimestampableInterface::class, )); } diff --git a/src/EventListener/UpdateManuallyDiscountedPropertySubscriber.php b/src/EventListener/UpdateManuallyDiscountedPropertySubscriber.php index fb81d8a..173210b 100644 --- a/src/EventListener/UpdateManuallyDiscountedPropertySubscriber.php +++ b/src/EventListener/UpdateManuallyDiscountedPropertySubscriber.php @@ -46,15 +46,15 @@ private static function update(LifecycleEventArgs $event): void return; } - if ($event instanceof PreUpdateEventArgs - && !$event->hasChangedField('price') - && !$event->hasChangedField('originalPrice') + if ($event instanceof PreUpdateEventArgs && + !$event->hasChangedField('price') && + !$event->hasChangedField('originalPrice') ) { return; } $channelPricing->setManuallyDiscounted( - $channelPricing->hasDiscount() + $channelPricing->hasDiscount(), ); } } diff --git a/src/Factory/PromotionRuleFactory.php b/src/Factory/PromotionRuleFactory.php index dd563b1..1ab3343 100644 --- a/src/Factory/PromotionRuleFactory.php +++ b/src/Factory/PromotionRuleFactory.php @@ -22,7 +22,7 @@ final class PromotionRuleFactory implements PromotionRuleFactoryInterface public function __construct( FactoryInterface $decoratedFactory, - array $rules + array $rules, ) { $this->decoratedFactory = $decoratedFactory; $this->rules = $rules; @@ -64,7 +64,7 @@ public function createByType(string $type, array $configuration, bool $strict = if ($strict) { throw new InvalidArgumentException(sprintf( 'Type must be one of: %s', - implode(', ', array_keys($this->rules)) + implode(', ', array_keys($this->rules)), )); } @@ -77,7 +77,7 @@ public function createHasTaxon(array $taxonCodes): PromotionRuleInterface return $this->createPromotionRule( HasTaxonRule::TYPE, - ['taxons' => $taxonCodes] + ['taxons' => $taxonCodes], ); } @@ -87,7 +87,7 @@ public function createHasNotTaxon(array $taxonCodes): PromotionRuleInterface return $this->createPromotionRule( HasNotTaxonRule::TYPE, - ['taxons' => $taxonCodes] + ['taxons' => $taxonCodes], ); } @@ -95,7 +95,7 @@ public function createContainsProduct(string $productCode): PromotionRuleInterfa { return $this->createPromotionRule( ContainsProductRule::TYPE, - ['product' => $productCode] + ['product' => $productCode], ); } @@ -105,7 +105,7 @@ public function createContainsProducts(array $productCodes): PromotionRuleInterf return $this->createPromotionRule( ContainsProductsRule::TYPE, - ['products' => $productCodes] + ['products' => $productCodes], ); } diff --git a/src/Fixture/Factory/PromotionExampleFactory.php b/src/Fixture/Factory/PromotionExampleFactory.php index ea0176d..78b1ebd 100644 --- a/src/Fixture/Factory/PromotionExampleFactory.php +++ b/src/Fixture/Factory/PromotionExampleFactory.php @@ -37,7 +37,7 @@ public function __construct( ChannelRepositoryInterface $channelRepository, PromotionRepositoryInterface $promotionRepository, Factory $promotionFactory, - PromotionRuleExampleFactory $promotionRuleExampleFactory + PromotionRuleExampleFactory $promotionRuleExampleFactory, ) { $this->channelRepository = $channelRepository; $this->promotionRepository = $promotionRepository; @@ -101,20 +101,20 @@ protected function configureOptions(OptionsResolver $resolver): void ->setDefault('code', static function (Options $options): string { return StringInflector::nameToCode($options['name']); }) - ->setDefault('name', function (Options $options): string { + ->setDefault('name', function (): string { /** @var string $text */ $text = $this->faker->words(3, true); return $text; }) - ->setDefault('description', function (Options $options): string { + ->setDefault('description', function (): string { return $this->faker->sentence(); }) ->setDefault('priority', 0) ->setAllowedTypes('priority', 'int') - ->setDefault('exclusive', function (Options $options): bool { + ->setDefault('exclusive', function (): bool { return $this->faker->boolean(25); }) @@ -123,11 +123,11 @@ protected function configureOptions(OptionsResolver $resolver): void ->setDefault('ends_at', null) ->setAllowedTypes('ends_at', ['null', 'string']) - ->setDefault('enabled', function (Options $options): bool { + ->setDefault('enabled', function (): bool { return $this->faker->boolean(90); }) - ->setDefault('discount', function (Options $options): float { + ->setDefault('discount', function (): float { return $this->faker->randomFloat(3, 0, 100); }) ->setNormalizer('discount', static function (Options $options, $value): float { diff --git a/src/Fixture/Factory/PromotionRuleExampleFactory.php b/src/Fixture/Factory/PromotionRuleExampleFactory.php index c85947a..6324747 100644 --- a/src/Fixture/Factory/PromotionRuleExampleFactory.php +++ b/src/Fixture/Factory/PromotionRuleExampleFactory.php @@ -33,7 +33,7 @@ public function create(array $options = []): PromotionRuleInterface return $this->promotionRuleFactory->createByType( $options['type'], - $options['configuration'] + $options['configuration'], ); } diff --git a/src/Fixture/PromotionFixture.php b/src/Fixture/PromotionFixture.php index 71a890e..39f539c 100644 --- a/src/Fixture/PromotionFixture.php +++ b/src/Fixture/PromotionFixture.php @@ -11,11 +11,12 @@ final class PromotionFixture extends AbstractResourceFixture { public function getName(): string { - return 'catalog_promotion'; + return 'setono_catalog_promotion'; } protected function configureResourceNode(ArrayNodeDefinition $resourceNode): void { + /** @psalm-suppress MixedMethodCall,UndefinedInterfaceMethod,PossiblyNullReference */ $resourceNode ->children() ->scalarNode('code')->cannotBeEmpty()->end() @@ -43,7 +44,8 @@ protected function configureResourceNode(ArrayNodeDefinition $resourceNode): voi ->end() ->end() ->end() - ->arrayNode('channels')->scalarPrototype()->end()->end() + ->arrayNode('channels') + ->scalarPrototype()->end() ; } } diff --git a/src/Form/Type/Rule/ContainsProductConfigurationType.php b/src/Form/Type/Rule/ContainsProductConfigurationType.php index 70c60d7..04bcc7b 100644 --- a/src/Form/Type/Rule/ContainsProductConfigurationType.php +++ b/src/Form/Type/Rule/ContainsProductConfigurationType.php @@ -29,7 +29,7 @@ public function buildForm(FormBuilderInterface $builder, array $options): void ; $builder->get('product')->addModelTransformer( - new ReversedTransformer(new ResourceToIdentifierTransformer($this->productRepository, 'code')) + new ReversedTransformer(new ResourceToIdentifierTransformer($this->productRepository, 'code')), ); } diff --git a/src/Model/ChannelPricingInterface.php b/src/Model/ChannelPricingInterface.php index 0c5b230..3f8b7a8 100644 --- a/src/Model/ChannelPricingInterface.php +++ b/src/Model/ChannelPricingInterface.php @@ -4,7 +4,9 @@ namespace Setono\SyliusCatalogPromotionPlugin\Model; +use Doctrine\Common\Collections\Collection; use Sylius\Component\Core\Model\ChannelPricingInterface as BaseChannelPricingInterface; +use Sylius\Component\Promotion\Model\CatalogPromotionInterface; use Sylius\Component\Resource\Model\TimestampableInterface; interface ChannelPricingInterface extends BaseChannelPricingInterface, TimestampableInterface @@ -31,7 +33,7 @@ public function getBulkIdentifier(): ?string; public function resetBulkIdentifier(): void; - public function getAppliedPromotions(): array; + public function getAppliedPromotions(): Collection; - public function addAppliedPromotion(string $promotionCode): void; + public function addAppliedPromotion(CatalogPromotionInterface $promotionCode): void; } diff --git a/src/Model/ChannelPricingTrait.php b/src/Model/ChannelPricingTrait.php index 9c32612..722168f 100644 --- a/src/Model/ChannelPricingTrait.php +++ b/src/Model/ChannelPricingTrait.php @@ -5,9 +5,11 @@ namespace Setono\SyliusCatalogPromotionPlugin\Model; use DateTimeInterface; +use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; use Gedmo\Mapping\Annotation as Gedmo; use Sylius\Component\Core\Model\ChannelPricing; +use Sylius\Component\Promotion\Model\CatalogPromotionInterface; use Sylius\Component\Resource\Model\TimestampableTrait; /** @@ -35,10 +37,11 @@ trait ChannelPricingTrait * * @var array */ - protected array $appliedPromotions = []; + protected $appliedPromotions; /** * @ORM\Column(type="datetime", nullable=true) + * * @Gedmo\Timestampable(on="create") * * @var DateTimeInterface|null @@ -47,18 +50,24 @@ trait ChannelPricingTrait /** * @ORM\Column(type="datetime", nullable=true) + * * @Gedmo\Timestampable(on="update") * * @var DateTimeInterface|null */ protected $updatedAt; + public function __construct() + { + $this->appliedPromotions = []; + } + public function hasDiscount(): bool { - return null !== $this->getOriginalPrice() - && null !== $this->getPrice() - && $this->getOriginalPrice() > $this->getPrice() - ; + return null !== $this->getOriginalPrice() && + null !== $this->getPrice() && + $this->getOriginalPrice() > $this->getPrice() + ; } public function getDiscountAmount(): ?int @@ -110,12 +119,12 @@ public function resetBulkIdentifier(): void $this->bulkIdentifier = null; } - public function getAppliedPromotions(): array + public function getAppliedPromotions(): Collection { return $this->appliedPromotions; } - public function addAppliedPromotion(string $promotionCode): void + public function addAppliedPromotion(CatalogPromotionInterface $promotionCode): void { $this->appliedPromotions[] = $promotionCode; } diff --git a/src/Model/Promotion.php b/src/Model/Promotion.php index 73865e5..7785896 100644 --- a/src/Model/Promotion.php +++ b/src/Model/Promotion.php @@ -43,7 +43,6 @@ class Promotion implements PromotionInterface /** * @var Collection|PromotionRuleInterface[] - * * @psalm-var Collection */ protected Collection $rules; @@ -52,7 +51,6 @@ class Promotion implements PromotionInterface /** * @var BaseChannelInterface[]|Collection - * * @psalm-var Collection */ protected Collection $channels; diff --git a/src/Model/PromotionInterface.php b/src/Model/PromotionInterface.php index c399585..6c06769 100644 --- a/src/Model/PromotionInterface.php +++ b/src/Model/PromotionInterface.php @@ -61,7 +61,6 @@ public function setEnabled(bool $enabled): void; /** * @return Collection|PromotionRuleInterface[] - * * @psalm-return Collection */ public function getRules(): Collection; diff --git a/src/Registry/RuleServiceRegistry.php b/src/Registry/RuleServiceRegistry.php index 9daaea3..dbbfdbe 100644 --- a/src/Registry/RuleServiceRegistry.php +++ b/src/Registry/RuleServiceRegistry.php @@ -13,7 +13,7 @@ public function __construct() { parent::__construct( RuleInterface::class, - 'rule' + 'rule', ); } } diff --git a/src/Repository/ChannelPricingRepositoryInterface.php b/src/Repository/ChannelPricingRepositoryInterface.php index 6b30881..7f672dd 100644 --- a/src/Repository/ChannelPricingRepositoryInterface.php +++ b/src/Repository/ChannelPricingRepositoryInterface.php @@ -24,7 +24,7 @@ public function updateMultiplier( DateTimeInterface $dateTime, string $bulkIdentifier, bool $exclusive = false, - bool $manuallyDiscountedProductsExcluded = true + bool $manuallyDiscountedProductsExcluded = true, ): void; /** diff --git a/src/Resources/config/app/fixtures.yaml b/src/Resources/config/app/fixtures.yaml index 991f236..e9781b3 100644 --- a/src/Resources/config/app/fixtures.yaml +++ b/src/Resources/config/app/fixtures.yaml @@ -58,7 +58,7 @@ sylius_fixtures: - "FASHION_WEB" catalog_promotion_random: - name: catalog_promotion + name: setono_catalog_promotion options: random: 10 prototype: @@ -69,7 +69,7 @@ sylius_fixtures: - "jeans" catalog_promotion_tshirts: - name: catalog_promotion + name: setono_catalog_promotion options: custom: thirts: @@ -94,7 +94,7 @@ sylius_fixtures: - "FASHION_WEB" catalog_promotion_caps: - name: catalog_promotion + name: setono_catalog_promotion options: custom: accidentally_enabled: diff --git a/src/Rule/HasNotTaxonRule.php b/src/Rule/HasNotTaxonRule.php index 53d1df6..93fed64 100644 --- a/src/Rule/HasNotTaxonRule.php +++ b/src/Rule/HasNotTaxonRule.php @@ -46,7 +46,7 @@ public function filter(QueryBuilder $queryBuilder, array $configuration): void sprintf('SELECT %s.id ', $productVariantAlias) . sprintf('FROM %s AS %s ', ProductVariant::class, $productVariantAlias) . sprintf('LEFT JOIN %s AS %s WITH %s.product=%s.product ', ProductTaxon::class, $productTaxonAlias, $productTaxonAlias, $productVariantAlias) . - sprintf('WHERE %s.taxon IN (:%s)', $productTaxonAlias, $parameter) + sprintf('WHERE %s.taxon IN (:%s)', $productTaxonAlias, $parameter), ); /** @psalm-suppress PossiblyNullArgument */ diff --git a/src/Rule/HasTaxonRule.php b/src/Rule/HasTaxonRule.php index fff3f14..f343cc1 100644 --- a/src/Rule/HasTaxonRule.php +++ b/src/Rule/HasTaxonRule.php @@ -46,7 +46,7 @@ public function filter(QueryBuilder $queryBuilder, array $configuration): void sprintf('SELECT %s.id ', $productVariantAlias) . sprintf('FROM %s AS %s ', ProductVariant::class, $productVariantAlias) . sprintf('LEFT JOIN %s AS %s WITH %s.product=%s.product ', ProductTaxon::class, $productTaxonAlias, $productTaxonAlias, $productVariantAlias) . - sprintf('WHERE %s.taxon IN (:%s)', $productTaxonAlias, $parameter) + sprintf('WHERE %s.taxon IN (:%s)', $productTaxonAlias, $parameter), ); /** @psalm-suppress PossiblyNullArgument */ diff --git a/tests/Application/.babelrc b/tests/Application/.babelrc deleted file mode 100644 index e563a62..0000000 --- a/tests/Application/.babelrc +++ /dev/null @@ -1,15 +0,0 @@ -{ - "presets": [ - ["env", { - "targets": { - "node": "6" - }, - "useBuiltIns": true - }] - ], - "plugins": [ - ["transform-object-rest-spread", { - "useBuiltIns": true - }] - ] -} diff --git a/tests/Application/.env b/tests/Application/.env index 5e8e363..bebf094 100644 --- a/tests/Application/.env +++ b/tests/Application/.env @@ -1,5 +1,16 @@ -# This file is a "template" of which env vars needs to be defined in your configuration or in an .env file -# Set variables here that may be different on each deployment target of the app, e.g. development, staging, production. +# In all environments, the following files are loaded if they exist, +# the later taking precedence over the former: +# +# * .env contains default values for the environment variables needed by the app +# * .env.local uncommitted file with local overrides +# * .env.$APP_ENV committed environment-specific defaults +# * .env.$APP_ENV.local uncommitted environment-specific overrides +# +# Real environment variables win over .env files. +# +# DO NOT DEFINE PRODUCTION SECRETS IN THIS FILE NOR IN ANY OTHER COMMITTED FILES. +# +# Run "composer dump-env prod" to compile .env files for production use (requires symfony/flex >=1.2). # https://symfony.com/doc/current/best_practices/configuration.html#infrastructure-related-configuration ###> symfony/framework-bundle ### @@ -9,15 +20,28 @@ APP_SECRET=EDITME ###< symfony/framework-bundle ### ###> doctrine/doctrine-bundle ### -# Format described at http://docs.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/configuration.html#connecting-using-a-url +# Format described at https://docs.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/configuration.html#connecting-using-a-url # For a sqlite database, use: "sqlite:///%kernel.project_dir%/var/data.db" # Set "serverVersion" to your server version to avoid edge-case exceptions and extra database calls DATABASE_URL=mysql://root@127.0.0.1/setono_sylius_catalog_promotion_%kernel.environment%?serverVersion=5.7 ###< doctrine/doctrine-bundle ### +###> lexik/jwt-authentication-bundle ### +JWT_SECRET_KEY=%kernel.project_dir%/config/jwt/private.pem +JWT_PUBLIC_KEY=%kernel.project_dir%/config/jwt/public.pem +JWT_PASSPHRASE=acme_plugin_development +###< lexik/jwt-authentication-bundle ### + +###> symfony/messenger ### +# Choose one of the transports below +# MESSENGER_TRANSPORT_DSN=amqp://guest:guest@localhost:5672/%2f/messages +MESSENGER_TRANSPORT_DSN=doctrine://default +# MESSENGER_TRANSPORT_DSN=redis://localhost:6379/messages +###< symfony/messenger ### + ###> symfony/swiftmailer-bundle ### # For Gmail as a transport, use: "gmail://username:password@localhost" # For a generic SMTP server, use: "smtp://localhost:25?encryption=&auth_mode=" # Delivery is disabled by default via "null://localhost" -MAILER_URL=smtp://localhost +MAILER_DSN=smtp://localhost ###< symfony/swiftmailer-bundle ### diff --git a/tests/Application/.gitignore b/tests/Application/.gitignore index 8ad1225..bc600a8 100644 --- a/tests/Application/.gitignore +++ b/tests/Application/.gitignore @@ -1,4 +1,5 @@ /public/assets +/public/build /public/css /public/js /public/media/* diff --git a/tests/Application/Entity/ChannelPricing.php b/tests/Application/Entity/ChannelPricing.php index 1f4fd41..11cd8f7 100644 --- a/tests/Application/Entity/ChannelPricing.php +++ b/tests/Application/Entity/ChannelPricing.php @@ -11,6 +11,7 @@ /** * @ORM\Table(name="sylius_channel_pricing") + * * @ORM\Entity() */ class ChannelPricing extends BaseChannelPricing implements CatalogPromotionChannelPricingInterface diff --git a/tests/Application/Kernel.php b/tests/Application/Kernel.php index 29dd9b4..103202b 100644 --- a/tests/Application/Kernel.php +++ b/tests/Application/Kernel.php @@ -4,25 +4,10 @@ namespace Tests\Setono\SyliusCatalogPromotionPlugin\Application; -use PSS\SymfonyMockerContainer\DependencyInjection\MockerContainer; use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait; -use Symfony\Component\Config\Loader\DelegatingLoader; -use Symfony\Component\Config\Loader\LoaderInterface; -use Symfony\Component\Config\Loader\LoaderResolver; -use Symfony\Component\Config\Resource\FileResource; -use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\ContainerInterface; -use Symfony\Component\DependencyInjection\Loader\ClosureLoader; -use Symfony\Component\DependencyInjection\Loader\DirectoryLoader; -use Symfony\Component\DependencyInjection\Loader\GlobFileLoader; -use Symfony\Component\DependencyInjection\Loader\IniFileLoader; -use Symfony\Component\DependencyInjection\Loader\PhpFileLoader; -use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; -use Symfony\Component\DependencyInjection\Loader\YamlFileLoader; -use Symfony\Component\HttpKernel\Config\FileLocator; +use Symfony\Component\HttpKernel\Bundle\BundleInterface; use Symfony\Component\HttpKernel\Kernel as BaseKernel; -use Symfony\Component\Routing\RouteCollectionBuilder; -use Webmozart\Assert\Assert; +use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator; final class Kernel extends BaseKernel { @@ -42,65 +27,47 @@ public function getLogDir(): string public function registerBundles(): iterable { - $contents = require $this->getProjectDir() . '/config/bundles.php'; - foreach ($contents as $class => $envs) { - if (isset($envs['all']) || isset($envs[$this->environment])) { - yield new $class(); + foreach ($this->getConfigurationDirectories() as $confDir) { + $bundlesFile = $confDir . '/bundles.php'; + if (false === is_file($bundlesFile)) { + continue; } + yield from $this->registerBundlesFromFile($bundlesFile); } } - protected function configureContainer(ContainerBuilder $container, LoaderInterface $loader): void + protected function configureRoutes(RoutingConfigurator $routes): void { - $container->addResource(new FileResource($this->getProjectDir() . '/config/bundles.php')); - $container->setParameter('container.dumper.inline_class_loader', true); - $confDir = $this->getProjectDir() . '/config'; - - $loader->load($confDir . '/{packages}/*' . self::CONFIG_EXTS, 'glob'); - $loader->load($confDir . '/{packages}/' . $this->environment . '/**/*' . self::CONFIG_EXTS, 'glob'); - $loader->load($confDir . '/{services}' . self::CONFIG_EXTS, 'glob'); - $loader->load($confDir . '/{services}_' . $this->environment . self::CONFIG_EXTS, 'glob'); + foreach ($this->getConfigurationDirectories() as $confDir) { + $this->loadRoutesConfiguration($routes, $confDir); + } } - protected function configureRoutes(RouteCollectionBuilder $routes): void + private function loadRoutesConfiguration(RoutingConfigurator $routes, string $confDir): void { - $confDir = $this->getProjectDir() . '/config'; - - $routes->import($confDir . '/{routes}/*' . self::CONFIG_EXTS, '/', 'glob'); - $routes->import($confDir . '/{routes}/' . $this->environment . '/**/*' . self::CONFIG_EXTS, '/', 'glob'); - $routes->import($confDir . '/{routes}' . self::CONFIG_EXTS, '/', 'glob'); + $routes->import($confDir . '/{routes}/*' . self::CONFIG_EXTS); + $routes->import($confDir . '/{routes}/' . $this->environment . '/**/*' . self::CONFIG_EXTS); + $routes->import($confDir . '/{routes}' . self::CONFIG_EXTS); } - protected function getContainerBaseClass(): string + /** + * @return BundleInterface[] + */ + private function registerBundlesFromFile(string $bundlesFile): iterable { - if ($this->isTestEnvironment()) { - return MockerContainer::class; + $contents = require $bundlesFile; + foreach ($contents as $class => $envs) { + if (isset($envs['all']) || isset($envs[$this->environment])) { + yield new $class(); + } } - - return parent::getContainerBaseClass(); - } - - protected function getContainerLoader(ContainerInterface $container): LoaderInterface - { - /** @var ContainerBuilder $container */ - Assert::isInstanceOf($container, ContainerBuilder::class); - - $locator = new FileLocator($this, $this->getRootDir() . '/Resources'); - $resolver = new LoaderResolver(array( - new XmlFileLoader($container, $locator), - new YamlFileLoader($container, $locator), - new IniFileLoader($container, $locator), - new PhpFileLoader($container, $locator), - new GlobFileLoader($container, $locator), - new DirectoryLoader($container, $locator), - new ClosureLoader($container), - )); - - return new DelegatingLoader($resolver); } - private function isTestEnvironment(): bool + /** + * @return string[] + */ + private function getConfigurationDirectories(): iterable { - return 0 === strpos($this->getEnvironment(), 'test'); + yield $this->getProjectDir() . '/config'; } } diff --git a/tests/Application/assets/admin/entry.js b/tests/Application/assets/admin/entry.js new file mode 100644 index 0000000..635f5ac --- /dev/null +++ b/tests/Application/assets/admin/entry.js @@ -0,0 +1 @@ +import 'sylius/bundle/AdminBundle/Resources/private/entry'; diff --git a/tests/Application/assets/shop/entry.js b/tests/Application/assets/shop/entry.js new file mode 100644 index 0000000..aadc317 --- /dev/null +++ b/tests/Application/assets/shop/entry.js @@ -0,0 +1 @@ +import 'sylius/bundle/ShopBundle/Resources/private/entry'; diff --git a/tests/Application/bin/console b/tests/Application/bin/console index 1c90522..ad9a84d 100755 --- a/tests/Application/bin/console +++ b/tests/Application/bin/console @@ -4,7 +4,7 @@ use Tests\Setono\SyliusCatalogPromotionPlugin\Application\Kernel; use Symfony\Bundle\FrameworkBundle\Console\Application; use Symfony\Component\Console\Input\ArgvInput; -use Symfony\Component\Debug\Debug; +use Symfony\Component\ErrorHandler\Debug; set_time_limit(0); diff --git a/tests/Application/composer.json b/tests/Application/composer.json index 22201e1..326735f 100644 --- a/tests/Application/composer.json +++ b/tests/Application/composer.json @@ -1,5 +1,5 @@ { - "name": "setono/sylius-catalog-promotion-plugin-test-application", - "description": "Sylius application for plugin testing purposes (composer.json needed for project dir resolving)", - "license": "MIT" + "name": "sylius/plugin-skeleton-test-application", + "description": "Sylius application for plugin testing purposes (composer.json needed for project dir resolving)", + "license": "MIT" } diff --git a/tests/Application/config/api_platform/.gitignore b/tests/Application/config/api_platform/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/tests/Application/config/bootstrap.php b/tests/Application/config/bootstrap.php index 8ea2af2..2291ab4 100644 --- a/tests/Application/config/bootstrap.php +++ b/tests/Application/config/bootstrap.php @@ -1,21 +1,23 @@ =1.2) -if (is_array($env = @include dirname(__DIR__).'/.env.local.php')) { +if (is_array($env = @include dirname(__DIR__) . '/.env.local.php')) { $_SERVER += $env; $_ENV += $env; } elseif (!class_exists(Dotenv::class)) { throw new RuntimeException('Please run "composer require symfony/dotenv" to load the ".env" files configuring the application.'); } else { // load all the .env files - (new Dotenv(true))->loadEnv(dirname(__DIR__).'/.env'); + (new Dotenv())->loadEnv(dirname(__DIR__) . '/.env'); } $_SERVER['APP_ENV'] = $_ENV['APP_ENV'] = ($_SERVER['APP_ENV'] ?? $_ENV['APP_ENV'] ?? null) ?: 'dev'; $_SERVER['APP_DEBUG'] = $_SERVER['APP_DEBUG'] ?? $_ENV['APP_DEBUG'] ?? 'prod' !== $_SERVER['APP_ENV']; -$_SERVER['APP_DEBUG'] = $_ENV['APP_DEBUG'] = (int) $_SERVER['APP_DEBUG'] || filter_var($_SERVER['APP_DEBUG'], FILTER_VALIDATE_BOOLEAN) ? '1' : '0'; +$_SERVER['APP_DEBUG'] = $_ENV['APP_DEBUG'] = (int) $_SERVER['APP_DEBUG'] || filter_var($_SERVER['APP_DEBUG'], \FILTER_VALIDATE_BOOLEAN) ? '1' : '0'; diff --git a/tests/Application/config/bundles.php b/tests/Application/config/bundles.php index cdf100b..6239dba 100644 --- a/tests/Application/config/bundles.php +++ b/tests/Application/config/bundles.php @@ -1,13 +1,13 @@ ['all' => true], Symfony\Bundle\MonologBundle\MonologBundle::class => ['all' => true], Symfony\Bundle\SecurityBundle\SecurityBundle::class => ['all' => true], - Symfony\Bundle\SwiftmailerBundle\SwiftmailerBundle::class => ['all' => true], Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true], Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class => ['all' => true], - Doctrine\Bundle\DoctrineCacheBundle\DoctrineCacheBundle::class => ['all' => true], Sylius\Bundle\OrderBundle\SyliusOrderBundle::class => ['all' => true], Sylius\Bundle\MoneyBundle\SyliusMoneyBundle::class => ['all' => true], Sylius\Bundle\CurrencyBundle\SyliusCurrencyBundle::class => ['all' => true], @@ -29,9 +29,7 @@ Sylius\Bundle\ReviewBundle\SyliusReviewBundle::class => ['all' => true], Sylius\Bundle\CoreBundle\SyliusCoreBundle::class => ['all' => true], Sylius\Bundle\ResourceBundle\SyliusResourceBundle::class => ['all' => true], - Setono\SyliusCatalogPromotionPlugin\SetonoSyliusCatalogPromotionPlugin::class => ['all' => true], - Sylius\Bundle\GridBundle\SyliusGridBundle::class => ['all' => true], winzou\Bundle\StateMachineBundle\winzouStateMachineBundle::class => ['all' => true], Sonata\BlockBundle\SonataBlockBundle::class => ['all' => true], @@ -40,20 +38,25 @@ FOS\RestBundle\FOSRestBundle::class => ['all' => true], Knp\Bundle\GaufretteBundle\KnpGaufretteBundle::class => ['all' => true], Knp\Bundle\MenuBundle\KnpMenuBundle::class => ['all' => true], + League\FlysystemBundle\FlysystemBundle::class => ['all' => true], Liip\ImagineBundle\LiipImagineBundle::class => ['all' => true], Payum\Bundle\PayumBundle\PayumBundle::class => ['all' => true], Stof\DoctrineExtensionsBundle\StofDoctrineExtensionsBundle::class => ['all' => true], - WhiteOctober\PagerfantaBundle\WhiteOctoberPagerfantaBundle::class => ['all' => true], Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle::class => ['all' => true], Sylius\Bundle\FixturesBundle\SyliusFixturesBundle::class => ['all' => true], Sylius\Bundle\PayumBundle\SyliusPayumBundle::class => ['all' => true], Sylius\Bundle\ThemeBundle\SyliusThemeBundle::class => ['all' => true], Sylius\Bundle\AdminBundle\SyliusAdminBundle::class => ['all' => true], Sylius\Bundle\ShopBundle\SyliusShopBundle::class => ['all' => true], - FOS\OAuthServerBundle\FOSOAuthServerBundle::class => ['all' => true], - Sylius\Bundle\AdminApiBundle\SyliusAdminApiBundle::class => ['all' => true], - Setono\JobStatusBundle\SetonoJobStatusBundle::class => ['all' => true], Symfony\Bundle\DebugBundle\DebugBundle::class => ['dev' => true, 'test' => true, 'test_cached' => true], Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true, 'test_cached' => true], + ApiPlatform\Core\Bridge\Symfony\Bundle\ApiPlatformBundle::class => ['all' => true], + Lexik\Bundle\JWTAuthenticationBundle\LexikJWTAuthenticationBundle::class => ['all' => true], + Sylius\Bundle\ApiBundle\SyliusApiBundle::class => ['all' => true], + SyliusLabs\DoctrineMigrationsExtraBundle\SyliusLabsDoctrineMigrationsExtraBundle::class => ['all' => true], + BabDev\PagerfantaBundle\BabDevPagerfantaBundle::class => ['all' => true], + SyliusLabs\Polyfill\Symfony\Security\Bundle\SyliusLabsPolyfillSymfonySecurityBundle::class => ['all' => true], + Symfony\WebpackEncoreBundle\WebpackEncoreBundle::class => ['all' => true], + Sylius\Calendar\SyliusCalendarBundle::class => ['all' => true], FriendsOfBehat\SymfonyExtension\Bundle\FriendsOfBehatSymfonyExtensionBundle::class => ['test' => true, 'test_cached' => true], ]; diff --git a/tests/Application/config/jwt/private.pem b/tests/Application/config/jwt/private.pem new file mode 100644 index 0000000..2bcf023 --- /dev/null +++ b/tests/Application/config/jwt/private.pem @@ -0,0 +1,54 @@ +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIJrTBXBgkqhkiG9w0BBQ0wSjApBgkqhkiG9w0BBQwwHAQIDbthk+aF5EACAggA +MAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAEqBBA3DYfh2mXByUxFNke/Wf5SBIIJ +UBckIgXeXBWPLQAAq07pN8uNFMUcUirFuEvbmxVe1PupCCAqriNxi1DqeSu/M7c1 +h66y0BqKZu/0G9SVTg63iCKDEiRAM3hLyD2CsjYg8h2LAaqQ9dFYGV0cHRhCXagZ +Sdt9YTfn2rarRbxauMSt0z9zwCaiUrBU4JwSM3g+tD7W0lxAm9TeaqBZek5DIX+j +3Gom5tPYQe8jvfGMGdMPuanoEwH4WbWzGcqypWriy4JwaggwKCQ4ituWfa9kqMMC +8HRmBBDg0gtafmQP910RZh18JL2ewF5Pl7GDsLtOj5gNLNuAiQxDCcYRnD4/Cdsl +bH91btmGX1nUVIFViUTW93eBsjBgdgqOMRVxUKkSSX6CmIZWlE3AazgwSbvOvNrN +JGa8X21UwfuS/JHLmfRmgdti0YxRjJkBYLPpcd3ILsi+MMhSHy0uycAM/dB80Q1B +vkW1UXGbCw/PzA5yHrzULzAl69E3Tt5nTVMIIcBGxw2rf+ej+AVjsuOl7etwecdC +gnA90ViNlGOACLVnhsjd4WVF9Oircosf0UYoblwcT6gw1GSVF9pWuu7k5hy/7Pt/ +o1BvonUgz/4VHG+K58qvtnlto+JE0XWzPvukNUyggtekTLyoQCI3ZKge6ui3qLax +N6whHpzFnFVF3GJAisTk5naHFawHNvH7t85pmc+UnjNUUmyl9RStl9LMYDSBKNlR +LzPlJK27E5SLhhyJCni4+UYjH6PdlJuKXJ0365fufJ+5ajHRatwt039xLnK0W+oa +L35NxCuXrn8YxOgJIomt7IrkV3AuxoWxcx4lRFoM0WCdn9SWZVtfFFiyX/Xr1qDg +dUysw3/bePEkOKr5JWx09hT0OKDpkwLFo2Ljtvjln4EMXYEvvVqFciKw0kqF73Dw +NyoSubwR4qs6FQclKW1TAP6UW4B6ffq1iagKOCTZ5bBtsPBZk8UGCJb57q4fUj4P +nJy0hnSdlOH4Am+US4HF4ayOGuaV1Be1taurdJnt5cNnUYRah0wg4nG+wVdG5HJk +f4dJ4nih9d6WA/8LfxdpB7NCwdR+KK6lky+GgLSdhmIT9lzjj2GDsU4lBf29TkBn +lyt98/LWGrgCQgZAQ/obxLT8CZtY+tNejGoMppY+ub8DIaLBFID+fcz13kgA9x7a +TeVB8RPok+S3yHXP9a4WSFe9DGjjN+m7EnRtte7MEjyMoekXVnT04gNbTMoGAjNb +lrR4g3ICygZtsoGSB2VEu7o3azAspXNBMOuJfRCuC0LDXcjH3TbvjX0da5wHBoK9 +clRxu+CDo9A849HMkmSje8wED7ysZnkvSX0OdPjXahVd4t1tDRI6jSlzFo9fGcjp +S8Ikm9iMrHXaWcDdtcq4C63CjSynIBr4mNIxe/f2e9nynm3AIv+aOan891RWHqrd +DdpSSPShtzATI9PbB+b+S0Gw58Y8fpO7yoZ87VW1BMpadmFZ87YY78jdB7BwInNI +JqtnivinM6qCsvbdMoGinUyL6PUcfQGiEAibouKr3zNRDC4aesBZZmj7w0dnf+HK +YC905aR0cddlc6DBo/ed3o9krMcZ6oY/vruemPTc5G7Cg3t4H3mInRgURw22X1wo +FsioU1yOdkK+MYxvmGsQvQuSJhp7h1Uz37t/olkPRafZgy2nEtw6DQO0Dm4UfSsD +nysq6dn1WeZPkOipGBRgQmY1FTRzwPoCxi7+/EuHhD8hr962rHOglSuNqPG89J8r +wdbTDr8kgXj2A9p+jI3TVKEX+h6FEhrCHW9SHUqATOZ7RiNL6hKld9j0U4D9gQwZ +dflA0TxpVsHXm7pd1idkr46jIFgw7HA89Erm0Ty7RolfHkqlRca805AVmsKkviIz +sbF5uv4WzIE3ViO8P1KMUhCyElm72mpyNTXBhkxkup9hJ4fQieaN6pET6dQ2xyjs +SBIvQoXI0JQKpespcyAdoh88ULQjRUXEOaNFfN7q+itTcocwmPZfzW2nXORJT2p8 +SXLqSE73nYZdqzSYFq1hLcnlubJ7yPBYYG1fI0IydjSGKfnjtB0DReR32OToRZ7m +laduZ8O+IaBUY4Sp6QdYcVbGGpG/wsPmTQyScc/O2bfSI7AiPnL9EnwebI9sPSWQ +R0t0QMXZOSSqNY6jkYjsOCxeekRIdY6havo2Y52Ywti0QNrkT4BQ+175VVTmRMdy +LNaMFeEq6ehSEdaHaozvjHvP50HQT43tCK+RJiL+Gf9FqawoQRt693yO5LFbQsuw +QsUSMi41txpINMa+HEc2K5FvGoPr7FmajLK7X2fr+3c/yZ4fahoMKEAVFWl5kRYx +Fe1smlw1Vxl/qNQ32LFWsBIK+XnYBteYmlpVyYrTgXyjnp1rK2zz0118DPFuYiAP +O0r6nnBz0NbwnSKb7S4CjxBKDvDbWTzP35Q5L/vySnO2zRbM64Gw7sjeLiJittWS +gQfbFpEk9k8KVndKM4H50Jp0WznmYpm1Tman8hUOiCvmq0qdI3bJ5Bnj0K+q2zFV ++noGpMFdq1+8WaUFLQFGCPM+yJgCqDgT1RAgfsGcomckGcmenDtHaTbcSFabEdpM +Tsa2qLdg/Kju+7JyGrkmobXl/azuyjYTHfRvSZrvO5WUDFzhChrJpIL4nA3ZGRlS +gvy+OzyyBh4sRyHwLItwUwE81aya3W4llAkhQ7OycmqniJgjtJzLwnxv2RQsB8bF +pyoqQdKVxkqHdbUFeh9igI4ffRAK+8xDER5J+RUoZ4mO8qJebxar54XTb6I/Lepc +g8ITX8bJ/GH+M6JdP7tLCikDTSGS+i1ReMQXE5XuEajYOVbzQdyWU5jleZIx0f6X +mTa4WvMEGNyNxKZZXsy9FAaBkZqrNzEv8k0uFgFMNWQcMMtiqbei86yACdqe+jiW +HqHv8wfoBHR+eIARub2itOJ/cI+oKv96d4it4FqQ9Lml8RUFFZj7Hrd6EjDb6Nq4 +P9ti7eku/xZvS0saBNChvv44GhP6FZJS0i/gidVffLna7Wua98tPZEAXp57k+XUL +PzsRJ4a+hFuQjkyXFoz/v8YuUdyCFUSVVr9ArVu0v4+4euFWpQLav5sXv0Gh9X58 +Ek1KIf7Z/tZAJnSjTjFuSbDX/AoTMTxpRBKKnFW6zY0Nw2pjTVMtTVDkv9xkBpBK +wod7FPD5f0T7y9YOARVZnBxVRSkkcYpEJFy5pLNeadg9 +-----END ENCRYPTED PRIVATE KEY----- diff --git a/tests/Application/config/jwt/public.pem b/tests/Application/config/jwt/public.pem new file mode 100644 index 0000000..cb4e13d --- /dev/null +++ b/tests/Application/config/jwt/public.pem @@ -0,0 +1,14 @@ +-----BEGIN PUBLIC KEY----- +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA6QkmF/Xi5nAYb8Kzr7qC +d63V2K+d/nCXbpDUKKDPJAqOtTlMoQSuJRLNnhhp7z1i/Cp4Bhifr20Pu2dq8JYg +6pRT4ctqvYb/MXxAaPZc3EcBC0S6AhgKO/fDvR3LcqYqGJmQQOXZvxTsgqongdvV +4XbqFBMMgngyayoBk0VKTaI/s+LQhIce+1QaxbAI0+/zbR0hZ1hWT73orJi3do+1 +TBzQol+V7WGa8LlJfmgM56qO3BmVkeTDMBc27pGp6g3+Oufk/l29jEGJlUT9yu7Q +BRhaQTWNVASa2aD+AKjVBzJh53O2zD8slAbjF1M9U7bbWN28Sv+xC/dUz0q9HnPu +RsY2tnwryqTyYn/Hf2xyP3/KvjJ6oslAwemu5JirdJkO7KVQAthWG42gLuhZg3ks +cSZhCLZH7nO2UDsf+2ZZgdbhpYZwR4gDRfNt7GKWXnWZOz9Uw1yVCPgylyZRZwg8 +l0y9aABdj3379I22icrwpMZbAgkyxNSV6UNJuxZksLUoP3i9OvXYgPYU9E4tU/Ul +Dm/T1rGSReGoPkU1YQnI50bq7p1byIoUu2scTflvpTVI5a7zULkS1tg60xk7vBRC +aBc7nr4UEtA235N6uLtcGxH11WBMwsKX69sSU0sQdC4Sk25zXM2gc8R1XV9K3qz2 +wQorQRlCwrkG44VRDgbFH+8CAwEAAQ== +-----END PUBLIC KEY----- diff --git a/tests/Application/config/packages/_sylius.yaml b/tests/Application/config/packages/_sylius.yaml index b22a2e2..05072b8 100644 --- a/tests/Application/config/packages/_sylius.yaml +++ b/tests/Application/config/packages/_sylius.yaml @@ -1,10 +1,8 @@ imports: - { resource: "@SyliusCoreBundle/Resources/config/app/config.yml" } - - { resource: "@SyliusAdminBundle/Resources/config/app/config.yml" } - - { resource: "@SyliusAdminApiBundle/Resources/config/app/config.yml" } - - { resource: "@SyliusShopBundle/Resources/config/app/config.yml" } + - { resource: "@SyliusApiBundle/Resources/config/app/config.yaml" } parameters: sylius_core.public_dir: '%kernel.project_dir%/public' @@ -13,6 +11,9 @@ sylius_shop: product_grid: include_all_descendants: true +sylius_api: + enabled: true + sylius_core: resources: channel_pricing: diff --git a/tests/Application/config/packages/api_platform.yaml b/tests/Application/config/packages/api_platform.yaml new file mode 100644 index 0000000..26def49 --- /dev/null +++ b/tests/Application/config/packages/api_platform.yaml @@ -0,0 +1,9 @@ +api_platform: + mapping: + paths: + - '%kernel.project_dir%/../../vendor/sylius/sylius/src/Sylius/Bundle/ApiBundle/Resources/config/api_resources' + - '%kernel.project_dir%/config/api_platform' + patch_formats: + json: ['application/merge-patch+json'] + swagger: + versions: [3] diff --git a/tests/Application/config/packages/assets.yaml b/tests/Application/config/packages/assets.yaml new file mode 100644 index 0000000..2468901 --- /dev/null +++ b/tests/Application/config/packages/assets.yaml @@ -0,0 +1,7 @@ +framework: + assets: + packages: + shop: + json_manifest_path: '%kernel.project_dir%/public/build/shop/manifest.json' + admin: + json_manifest_path: '%kernel.project_dir%/public/build/admin/manifest.json' diff --git a/tests/Application/config/packages/dev/jms_serializer.yaml b/tests/Application/config/packages/dev/jms_serializer.yaml index 353e460..2f32a9b 100644 --- a/tests/Application/config/packages/dev/jms_serializer.yaml +++ b/tests/Application/config/packages/dev/jms_serializer.yaml @@ -1,6 +1,11 @@ jms_serializer: visitors: - json: + json_serialization: + options: + - JSON_PRETTY_PRINT + - JSON_UNESCAPED_SLASHES + - JSON_PRESERVE_ZERO_FRACTION + json_deserialization: options: - JSON_PRETTY_PRINT - JSON_UNESCAPED_SLASHES diff --git a/tests/Application/config/packages/dev/swiftmailer.yaml b/tests/Application/config/packages/dev/swiftmailer.yaml deleted file mode 100644 index f438078..0000000 --- a/tests/Application/config/packages/dev/swiftmailer.yaml +++ /dev/null @@ -1,2 +0,0 @@ -swiftmailer: - disable_delivery: true diff --git a/tests/Application/config/packages/doctrine.yaml b/tests/Application/config/packages/doctrine.yaml index 115192a..872164d 100644 --- a/tests/Application/config/packages/doctrine.yaml +++ b/tests/Application/config/packages/doctrine.yaml @@ -12,7 +12,6 @@ doctrine: charset: UTF8 url: '%env(resolve:DATABASE_URL)%' - schema_filter: '~^(?!messenger_messages)~' orm: auto_generate_proxy_classes: true naming_strategy: doctrine.orm.naming_strategy.underscore diff --git a/tests/Application/config/packages/doctrine_migrations.yaml b/tests/Application/config/packages/doctrine_migrations.yaml index c0a1202..cdbc01a 100644 --- a/tests/Application/config/packages/doctrine_migrations.yaml +++ b/tests/Application/config/packages/doctrine_migrations.yaml @@ -1,5 +1,4 @@ doctrine_migrations: - dir_name: "%kernel.project_dir%/src/Migrations" - - # Namespace is arbitrary but should be different from App\Migrations as migrations classes should NOT be autoloaded - namespace: DoctrineMigrations + storage: + table_storage: + table_name: sylius_migrations diff --git a/tests/Application/config/packages/fos_rest.yaml b/tests/Application/config/packages/fos_rest.yaml index a72eef7..eaebb27 100644 --- a/tests/Application/config/packages/fos_rest.yaml +++ b/tests/Application/config/packages/fos_rest.yaml @@ -7,5 +7,5 @@ fos_rest: empty_content: 204 format_listener: rules: - - { path: '^/api/.*', priorities: ['json', 'xml'], fallback_format: json, prefer_extension: true } + - { path: '^/api/v1/.*', priorities: ['json', 'xml'], fallback_format: json, prefer_extension: true } - { path: '^/', stop: true } diff --git a/tests/Application/config/packages/framework.yaml b/tests/Application/config/packages/framework.yaml index e74ed81..3df2c0a 100644 --- a/tests/Application/config/packages/framework.yaml +++ b/tests/Application/config/packages/framework.yaml @@ -1,7 +1,9 @@ framework: secret: '%env(APP_SECRET)%' - form: true + ide: phpstorm + form: + enabled: true + legacy_error_messages: false csrf_protection: true - templating: { engines: ["twig"] } session: handler_id: ~ diff --git a/tests/Application/config/packages/jms_serializer.yaml b/tests/Application/config/packages/jms_serializer.yaml index 64dd8d1..ed7bc61 100644 --- a/tests/Application/config/packages/jms_serializer.yaml +++ b/tests/Application/config/packages/jms_serializer.yaml @@ -1,4 +1,4 @@ jms_serializer: visitors: - xml: + xml_serialization: format_output: '%kernel.debug%' diff --git a/tests/Application/config/packages/lexik_jwt_authentication.yaml b/tests/Application/config/packages/lexik_jwt_authentication.yaml new file mode 100644 index 0000000..edfb69d --- /dev/null +++ b/tests/Application/config/packages/lexik_jwt_authentication.yaml @@ -0,0 +1,4 @@ +lexik_jwt_authentication: + secret_key: '%env(resolve:JWT_SECRET_KEY)%' + public_key: '%env(resolve:JWT_PUBLIC_KEY)%' + pass_phrase: '%env(JWT_PASSPHRASE)%' diff --git a/tests/Application/config/packages/mailer.yaml b/tests/Application/config/packages/mailer.yaml new file mode 100644 index 0000000..56a650d --- /dev/null +++ b/tests/Application/config/packages/mailer.yaml @@ -0,0 +1,3 @@ +framework: + mailer: + dsn: '%env(MAILER_DSN)%' diff --git a/tests/Application/config/packages/prod/jms_serializer.yaml b/tests/Application/config/packages/prod/jms_serializer.yaml index bc97faf..c288182 100644 --- a/tests/Application/config/packages/prod/jms_serializer.yaml +++ b/tests/Application/config/packages/prod/jms_serializer.yaml @@ -1,6 +1,10 @@ jms_serializer: visitors: - json: + json_serialization: + options: + - JSON_UNESCAPED_SLASHES + - JSON_PRESERVE_ZERO_FRACTION + json_deserialization: options: - JSON_UNESCAPED_SLASHES - JSON_PRESERVE_ZERO_FRACTION diff --git a/tests/Application/config/packages/security.yaml b/tests/Application/config/packages/security.yaml index 830b03d..2f5c687 100644 --- a/tests/Application/config/packages/security.yaml +++ b/tests/Application/config/packages/security.yaml @@ -1,16 +1,17 @@ -parameters: - sylius.security.admin_regex: "^/admin" - sylius.security.api_regex: "^/api" - sylius.security.shop_regex: "^/(?!admin|api/.*|api$|media/.*)[^/]++" - security: + enable_authenticator_manager: true providers: sylius_admin_user_provider: id: sylius.admin_user_provider.email_or_name_based + sylius_api_admin_user_provider: + id: sylius.admin_user_provider.email_or_name_based sylius_shop_user_provider: id: sylius.shop_user_provider.email_or_name_based - encoders: - Sylius\Component\User\Model\UserInterface: sha512 + sylius_api_shop_user_provider: + id: sylius.shop_user_provider.email_or_name_based + + password_hashers: + Sylius\Component\User\Model\UserInterface: argon2i firewalls: admin: switch_user: true @@ -25,30 +26,44 @@ security: default_target_path: sylius_admin_dashboard use_forward: false use_referer: true - csrf_token_generator: security.csrf.token_manager + enable_csrf: true csrf_parameter: _csrf_admin_security_token csrf_token_id: admin_authenticate remember_me: secret: "%env(APP_SECRET)%" - path: /admin + path: "/%sylius_admin.path_name%" name: APP_ADMIN_REMEMBER_ME lifetime: 31536000 remember_me_parameter: _remember_me logout: path: sylius_admin_logout target: sylius_admin_login - anonymous: true - oauth_token: - pattern: "%sylius.security.api_regex%/oauth/v2/token" - security: false + new_api_admin_user: + pattern: "%sylius.security.new_api_admin_regex%/.*" + provider: sylius_api_admin_user_provider + stateless: true + entry_point: jwt + json_login: + check_path: "%sylius.security.new_api_admin_route%/authentication-token" + username_path: email + password_path: password + success_handler: lexik_jwt_authentication.handler.authentication_success + failure_handler: lexik_jwt_authentication.handler.authentication_failure + jwt: true - api: - pattern: "%sylius.security.api_regex%/.*" - provider: sylius_admin_user_provider - fos_oauth: true + new_api_shop_user: + pattern: "%sylius.security.new_api_shop_regex%/.*" + provider: sylius_api_shop_user_provider stateless: true - anonymous: true + entry_point: jwt + json_login: + check_path: "%sylius.security.new_api_shop_route%/authentication-token" + username_path: email + password_path: password + success_handler: lexik_jwt_authentication.handler.authentication_success + failure_handler: lexik_jwt_authentication.handler.authentication_failure + jwt: true shop: switch_user: { role: ROLE_ALLOWED_TO_SWITCH } @@ -65,7 +80,7 @@ security: default_target_path: sylius_shop_homepage use_forward: false use_referer: true - csrf_token_generator: security.csrf.token_manager + enable_csrf: true csrf_parameter: _csrf_shop_security_token csrf_token_id: shop_authenticate remember_me: @@ -75,28 +90,33 @@ security: remember_me_parameter: _remember_me logout: path: sylius_shop_logout - target: sylius_shop_login + target: sylius_shop_homepage invalidate_session: false - success_handler: sylius.handler.shop_user_logout - anonymous: true dev: pattern: ^/(_(profiler|wdt)|css|images|js)/ security: false access_control: - - { path: "%sylius.security.admin_regex%/_partial", role: IS_AUTHENTICATED_ANONYMOUSLY, ips: [127.0.0.1, ::1] } + - { path: "%sylius.security.admin_regex%/_partial", role: PUBLIC_ACCESS, ips: [127.0.0.1, ::1] } - { path: "%sylius.security.admin_regex%/_partial", role: ROLE_NO_ACCESS } - - { path: "%sylius.security.shop_regex%/_partial", role: IS_AUTHENTICATED_ANONYMOUSLY, ips: [127.0.0.1, ::1] } + - { path: "%sylius.security.shop_regex%/_partial", role: PUBLIC_ACCESS, ips: [127.0.0.1, ::1] } - { path: "%sylius.security.shop_regex%/_partial", role: ROLE_NO_ACCESS } - - { path: "%sylius.security.admin_regex%/login", role: IS_AUTHENTICATED_ANONYMOUSLY } - - { path: "%sylius.security.api_regex%/login", role: IS_AUTHENTICATED_ANONYMOUSLY } - - { path: "%sylius.security.shop_regex%/login", role: IS_AUTHENTICATED_ANONYMOUSLY } + - { path: "%sylius.security.admin_regex%/forgotten-password", role: PUBLIC_ACCESS } - - { path: "%sylius.security.shop_regex%/register", role: IS_AUTHENTICATED_ANONYMOUSLY } - - { path: "%sylius.security.shop_regex%/verify", role: IS_AUTHENTICATED_ANONYMOUSLY } + - { path: "%sylius.security.admin_regex%/login", role: PUBLIC_ACCESS } + - { path: "%sylius.security.shop_regex%/login", role: PUBLIC_ACCESS } + + - { path: "%sylius.security.shop_regex%/register", role: PUBLIC_ACCESS } + - { path: "%sylius.security.shop_regex%/verify", role: PUBLIC_ACCESS } - { path: "%sylius.security.admin_regex%", role: ROLE_ADMINISTRATION_ACCESS } - - { path: "%sylius.security.api_regex%/.*", role: ROLE_API_ACCESS } - { path: "%sylius.security.shop_regex%/account", role: ROLE_USER } + + - { path: "%sylius.security.new_api_admin_route%/reset-password-requests", role: PUBLIC_ACCESS } + - { path: "%sylius.security.new_api_admin_regex%/.*", role: ROLE_API_ACCESS } + - { path: "%sylius.security.new_api_admin_route%/authentication-token", role: PUBLIC_ACCESS } + - { path: "%sylius.security.new_api_user_account_regex%/.*", role: ROLE_USER } + - { path: "%sylius.security.new_api_shop_route%/authentication-token", role: PUBLIC_ACCESS } + - { path: "%sylius.security.new_api_shop_regex%/.*", role: PUBLIC_ACCESS } diff --git a/tests/Application/config/packages/staging/swiftmailer.yaml b/tests/Application/config/packages/staging/swiftmailer.yaml deleted file mode 100644 index f438078..0000000 --- a/tests/Application/config/packages/staging/swiftmailer.yaml +++ /dev/null @@ -1,2 +0,0 @@ -swiftmailer: - disable_delivery: true diff --git a/tests/Application/config/packages/swiftmailer.yaml b/tests/Application/config/packages/swiftmailer.yaml deleted file mode 100644 index 3bab0d3..0000000 --- a/tests/Application/config/packages/swiftmailer.yaml +++ /dev/null @@ -1,2 +0,0 @@ -swiftmailer: - url: '%env(MAILER_URL)%' diff --git a/tests/Application/config/packages/test/framework.yaml b/tests/Application/config/packages/test/framework.yaml index 76d7e5e..fc1d3c1 100644 --- a/tests/Application/config/packages/test/framework.yaml +++ b/tests/Application/config/packages/test/framework.yaml @@ -1,4 +1,4 @@ framework: test: ~ session: - storage_id: session.storage.mock_file + storage_factory_id: session.storage.factory.mock_file diff --git a/tests/Application/config/packages/test/security.yaml b/tests/Application/config/packages/test/security.yaml new file mode 100644 index 0000000..4071d31 --- /dev/null +++ b/tests/Application/config/packages/test/security.yaml @@ -0,0 +1,6 @@ +security: + password_hashers: + Sylius\Component\User\Model\UserInterface: + algorithm: argon2i + time_cost: 3 + memory_cost: 10 diff --git a/tests/Application/config/packages/test/swiftmailer.yaml b/tests/Application/config/packages/test/swiftmailer.yaml deleted file mode 100644 index c438f4b..0000000 --- a/tests/Application/config/packages/test/swiftmailer.yaml +++ /dev/null @@ -1,6 +0,0 @@ -swiftmailer: - disable_delivery: true - logging: true - spool: - type: file - path: "%kernel.cache_dir%/spool" diff --git a/tests/Application/config/packages/test_cached/security.yaml b/tests/Application/config/packages/test_cached/security.yaml new file mode 100644 index 0000000..21cc377 --- /dev/null +++ b/tests/Application/config/packages/test_cached/security.yaml @@ -0,0 +1,3 @@ +security: + encoders: + sha512: sha512 diff --git a/tests/Application/config/packages/test_cached/swiftmailer.yaml b/tests/Application/config/packages/test_cached/swiftmailer.yaml deleted file mode 100644 index c438f4b..0000000 --- a/tests/Application/config/packages/test_cached/swiftmailer.yaml +++ /dev/null @@ -1,6 +0,0 @@ -swiftmailer: - disable_delivery: true - logging: true - spool: - type: file - path: "%kernel.cache_dir%/spool" diff --git a/tests/Application/config/packages/twig.yaml b/tests/Application/config/packages/twig.yaml index 3b315dc..8545473 100644 --- a/tests/Application/config/packages/twig.yaml +++ b/tests/Application/config/packages/twig.yaml @@ -2,3 +2,11 @@ twig: paths: ['%kernel.project_dir%/templates'] debug: '%kernel.debug%' strict_variables: '%kernel.debug%' + +services: + _defaults: + public: false + autowire: true + autoconfigure: true + + Twig\Extra\Intl\IntlExtension: ~ diff --git a/tests/Application/config/packages/webpack_encore.yaml b/tests/Application/config/packages/webpack_encore.yaml new file mode 100644 index 0000000..9bee248 --- /dev/null +++ b/tests/Application/config/packages/webpack_encore.yaml @@ -0,0 +1,5 @@ +webpack_encore: + output_path: '%kernel.project_dir%/public/build/default' + builds: + shop: '%kernel.project_dir%/public/build/shop' + admin: '%kernel.project_dir%/public/build/admin' diff --git a/tests/Application/config/routes/sylius_admin.yaml b/tests/Application/config/routes/sylius_admin.yaml index 1ba48d6..b9c12c2 100644 --- a/tests/Application/config/routes/sylius_admin.yaml +++ b/tests/Application/config/routes/sylius_admin.yaml @@ -1,3 +1,3 @@ sylius_admin: resource: "@SyliusAdminBundle/Resources/config/routing.yml" - prefix: /admin + prefix: '/%sylius_admin.path_name%' diff --git a/tests/Application/config/routes/sylius_api.yaml b/tests/Application/config/routes/sylius_api.yaml new file mode 100644 index 0000000..ae01ffc --- /dev/null +++ b/tests/Application/config/routes/sylius_api.yaml @@ -0,0 +1,3 @@ +sylius_api: + resource: "@SyliusApiBundle/Resources/config/routing.yml" + prefix: "%sylius.security.new_api_route%" diff --git a/tests/Application/config/routes/sylius_shop.yaml b/tests/Application/config/routes/sylius_shop.yaml index 8818568..22e7a4a 100644 --- a/tests/Application/config/routes/sylius_shop.yaml +++ b/tests/Application/config/routes/sylius_shop.yaml @@ -2,7 +2,7 @@ sylius_shop: resource: "@SyliusShopBundle/Resources/config/routing.yml" prefix: /{_locale} requirements: - _locale: ^[a-z]{2}(?:_[A-Z]{2})?$ + _locale: ^[A-Za-z]{2,4}(_([A-Za-z]{4}|[0-9]{3}))?(_([A-Za-z]{2}|[0-9]{3}))?$ sylius_shop_payum: resource: "@SyliusShopBundle/Resources/config/routing/payum.yml" @@ -11,4 +11,13 @@ sylius_shop_default_locale: path: / methods: [GET] defaults: - _controller: sylius.controller.shop.locale_switch:switchAction + _controller: sylius.controller.shop.locale_switch::switchAction + +# see https://web.dev/change-password-url/ +sylius_shop_request_password_reset_token_redirect: + path: /.well-known/change-password + methods: [GET] + controller: Symfony\Bundle\FrameworkBundle\Controller\RedirectController::redirectAction + defaults: + route: sylius_shop_request_password_reset_token + permanent: false diff --git a/tests/Application/config/secrets/dev/.gitignore b/tests/Application/config/secrets/dev/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/tests/Application/config/secrets/prod/.gitignore b/tests/Application/config/secrets/prod/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/tests/Application/config/secrets/test/.gitignore b/tests/Application/config/secrets/test/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/tests/Application/config/secrets/test_cached/.gitignore b/tests/Application/config/secrets/test_cached/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/tests/Application/config/serialization/.gitignore b/tests/Application/config/serialization/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/tests/Application/gulpfile.babel.js b/tests/Application/gulpfile.babel.js deleted file mode 100644 index 5920316..0000000 --- a/tests/Application/gulpfile.babel.js +++ /dev/null @@ -1,60 +0,0 @@ -import chug from 'gulp-chug'; -import gulp from 'gulp'; -import yargs from 'yargs'; - -const { argv } = yargs - .options({ - rootPath: { - description: ' path to public assets directory', - type: 'string', - requiresArg: true, - required: false, - }, - nodeModulesPath: { - description: ' path to node_modules directory', - type: 'string', - requiresArg: true, - required: false, - }, - }); - -const config = [ - '--rootPath', - argv.rootPath || '../../../../../../../tests/Application/public/assets', - '--nodeModulesPath', - argv.nodeModulesPath || '../../../../../../../tests/Application/node_modules', -]; - -export const buildAdmin = function buildAdmin() { - return gulp.src('../../vendor/sylius/sylius/src/Sylius/Bundle/AdminBundle/gulpfile.babel.js', { read: false }) - .pipe(chug({ args: config, tasks: 'build' })); -}; -buildAdmin.description = 'Build admin assets.'; - -export const watchAdmin = function watchAdmin() { - return gulp.src('../../vendor/sylius/sylius/src/Sylius/Bundle/AdminBundle/gulpfile.babel.js', { read: false }) - .pipe(chug({ args: config, tasks: 'watch' })); -}; -watchAdmin.description = 'Watch admin asset sources and rebuild on changes.'; - -export const buildShop = function buildShop() { - return gulp.src('../../vendor/sylius/sylius/src/Sylius/Bundle/ShopBundle/gulpfile.babel.js', { read: false }) - .pipe(chug({ args: config, tasks: 'build' })); -}; -buildShop.description = 'Build shop assets.'; - -export const watchShop = function watchShop() { - return gulp.src('../../vendor/sylius/sylius/src/Sylius/Bundle/ShopBundle/gulpfile.babel.js', { read: false }) - .pipe(chug({ args: config, tasks: 'watch' })); -}; -watchShop.description = 'Watch shop asset sources and rebuild on changes.'; - -export const build = gulp.parallel(buildAdmin, buildShop); -build.description = 'Build assets.'; - -gulp.task('admin', buildAdmin); -gulp.task('admin-watch', watchAdmin); -gulp.task('shop', buildShop); -gulp.task('shop-watch', watchShop); - -export default build; diff --git a/tests/Application/package.json b/tests/Application/package.json index 8512b4d..8f62c4c 100644 --- a/tests/Application/package.json +++ b/tests/Application/package.json @@ -1,57 +1,30 @@ { - "dependencies": { - "babel-polyfill": "^6.26.0", - "chart.js": "^2.9.3", - "jquery": "^3.4.0", - "jquery.dirtyforms": "^2.0.0", - "lightbox2": "^2.9.0", - "semantic-ui-css": "^2.2.0", - "slick-carousel": "^1.8.1" - }, - "devDependencies": { - "babel-core": "^6.26.3", - "babel-plugin-external-helpers": "^6.22.0", - "babel-plugin-module-resolver": "^3.1.1", - "babel-plugin-transform-object-rest-spread": "^6.26.0", - "babel-preset-env": "^1.7.0", - "babel-register": "^6.26.0", - "dedent": "^0.7.0", - "eslint": "^4.19.1", - "eslint-config-airbnb-base": "^12.1.0", - "eslint-import-resolver-babel-module": "^4.0.0", - "eslint-plugin-import": "^2.12.0", - "fast-async": "^6.3.7", - "gulp": "^4.0.0", - "gulp-chug": "^0.5", - "gulp-concat": "^2.6.0", - "gulp-debug": "^2.1.2", - "gulp-if": "^2.0.0", - "gulp-livereload": "^3.8.1", - "gulp-order": "^1.1.1", - "gulp-sass": "^4.0.1", - "gulp-sourcemaps": "^1.6.0", - "gulp-uglifycss": "^1.0.5", - "merge-stream": "^1.0.0", - "rollup": "^0.60.7", - "rollup-plugin-babel": "^3.0.4", - "rollup-plugin-commonjs": "^9.1.3", - "rollup-plugin-inject": "^2.0.0", - "rollup-plugin-node-resolve": "^3.3.0", - "rollup-plugin-uglify": "^4.0.0", - "sass-loader": "^7.0.1", - "upath": "^1.1.0", - "yargs": "^6.4.0" - }, - "scripts": { - "build": "gulp build", - "gulp": "gulp build", - "lint": "yarn lint:js", - "lint:js": "eslint gulpfile.babel.js" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/Sylius/Sylius.git" - }, - "author": "Paweł Jędrzejewski", - "license": "MIT" + "dependencies": { + "chart.js": "^3.9", + "jquery": "^3.6", + "jquery.dirtyforms": "^2.0", + "lightbox2": "^2.9", + "semantic-ui-css": "^2.2", + "slick-carousel": "^1.8" + }, + "devDependencies": { + "@symfony/webpack-encore": "^1.8", + "eslint": "^8.18", + "eslint-config-airbnb-base": "^15.0", + "eslint-import-resolver-babel-module": "^5.3", + "eslint-plugin-import": "^2.26", + "node-sass": "^7.0", + "sass-loader": "^12.0" + }, + "scripts": { + "dev": "encore dev", + "build": "encore production", + "watch": "encore dev --watch" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/Sylius/Sylius.git" + }, + "author": "Paweł Jędrzejewski", + "license": "MIT" } diff --git a/tests/Application/public/index.php b/tests/Application/public/index.php index 6387b54..113f9e7 100644 --- a/tests/Application/public/index.php +++ b/tests/Application/public/index.php @@ -1,10 +1,12 @@ +
+ Sylius +
+ diff --git a/tests/Application/templates/bundles/SyliusAdminBundle/_scripts.html.twig b/tests/Application/templates/bundles/SyliusAdminBundle/_scripts.html.twig new file mode 100644 index 0000000..f5f9835 --- /dev/null +++ b/tests/Application/templates/bundles/SyliusAdminBundle/_scripts.html.twig @@ -0,0 +1 @@ +{{ encore_entry_script_tags('admin-entry', null, 'admin') }} diff --git a/tests/Application/templates/bundles/SyliusAdminBundle/_styles.html.twig b/tests/Application/templates/bundles/SyliusAdminBundle/_styles.html.twig new file mode 100644 index 0000000..a96144c --- /dev/null +++ b/tests/Application/templates/bundles/SyliusAdminBundle/_styles.html.twig @@ -0,0 +1 @@ +{{ encore_entry_link_tags('admin-entry', null, 'admin') }} diff --git a/tests/Application/templates/bundles/SyliusShopBundle/Homepage/_banner.html.twig b/tests/Application/templates/bundles/SyliusShopBundle/Homepage/_banner.html.twig new file mode 100644 index 0000000..bb594f7 --- /dev/null +++ b/tests/Application/templates/bundles/SyliusShopBundle/Homepage/_banner.html.twig @@ -0,0 +1,2 @@ +Sylius + diff --git a/tests/Application/templates/bundles/SyliusShopBundle/Layout/Footer/Grid/_plus.html.twig b/tests/Application/templates/bundles/SyliusShopBundle/Layout/Footer/Grid/_plus.html.twig new file mode 100644 index 0000000..0bf5a06 --- /dev/null +++ b/tests/Application/templates/bundles/SyliusShopBundle/Layout/Footer/Grid/_plus.html.twig @@ -0,0 +1,9 @@ + diff --git a/tests/Application/templates/bundles/SyliusShopBundle/Layout/Header/_logo.html.twig b/tests/Application/templates/bundles/SyliusShopBundle/Layout/Header/_logo.html.twig new file mode 100644 index 0000000..39110d6 --- /dev/null +++ b/tests/Application/templates/bundles/SyliusShopBundle/Layout/Header/_logo.html.twig @@ -0,0 +1,5 @@ + diff --git a/tests/Application/templates/bundles/SyliusShopBundle/_scripts.html.twig b/tests/Application/templates/bundles/SyliusShopBundle/_scripts.html.twig new file mode 100644 index 0000000..d1655bb --- /dev/null +++ b/tests/Application/templates/bundles/SyliusShopBundle/_scripts.html.twig @@ -0,0 +1 @@ +{{ encore_entry_script_tags('shop-entry', null, 'shop') }} diff --git a/tests/Application/templates/bundles/SyliusShopBundle/_styles.html.twig b/tests/Application/templates/bundles/SyliusShopBundle/_styles.html.twig new file mode 100644 index 0000000..fd2c7cb --- /dev/null +++ b/tests/Application/templates/bundles/SyliusShopBundle/_styles.html.twig @@ -0,0 +1 @@ +{{ encore_entry_link_tags('shop-entry', null, 'shop') }} diff --git a/tests/Application/webpack.config.js b/tests/Application/webpack.config.js new file mode 100644 index 0000000..fae1429 --- /dev/null +++ b/tests/Application/webpack.config.js @@ -0,0 +1,47 @@ +const path = require('path'); +const Encore = require('@symfony/webpack-encore'); + +const syliusBundles = path.resolve(__dirname, '../../vendor/sylius/sylius/src/Sylius/Bundle/'); +const uiBundleScripts = path.resolve(syliusBundles, 'UiBundle/Resources/private/js/'); +const uiBundleResources = path.resolve(syliusBundles, 'UiBundle/Resources/private/'); + +// Shop config +Encore + .setOutputPath('public/build/shop/') + .setPublicPath('/build/shop') + .addEntry('shop-entry', './assets/shop/entry.js') + .disableSingleRuntimeChunk() + .cleanupOutputBeforeBuild() + .enableSourceMaps(!Encore.isProduction()) + .enableVersioning(Encore.isProduction()) + .enableSassLoader(); + +const shopConfig = Encore.getWebpackConfig(); + +shopConfig.resolve.alias['sylius/ui'] = uiBundleScripts; +shopConfig.resolve.alias['sylius/ui-resources'] = uiBundleResources; +shopConfig.resolve.alias['sylius/bundle'] = syliusBundles; +shopConfig.name = 'shop'; + +Encore.reset(); + +// Admin config +Encore + .setOutputPath('public/build/admin/') + .setPublicPath('/build/admin') + .addEntry('admin-entry', './assets/admin/entry.js') + .disableSingleRuntimeChunk() + .cleanupOutputBeforeBuild() + .enableSourceMaps(!Encore.isProduction()) + .enableVersioning(Encore.isProduction()) + .enableSassLoader(); + +const adminConfig = Encore.getWebpackConfig(); + +adminConfig.resolve.alias['sylius/ui'] = uiBundleScripts; +adminConfig.resolve.alias['sylius/ui-resources'] = uiBundleResources; +adminConfig.resolve.alias['sylius/bundle'] = syliusBundles; +adminConfig.externals = Object.assign({}, adminConfig.externals, { window: 'window', document: 'document' }); +adminConfig.name = 'admin'; + +module.exports = [shopConfig, adminConfig]; diff --git a/tests/Behat/Context/Cli/CommandContext.php b/tests/Behat/Context/Cli/CommandContext.php index 2635ddb..05ffb7f 100644 --- a/tests/Behat/Context/Cli/CommandContext.php +++ b/tests/Behat/Context/Cli/CommandContext.php @@ -7,7 +7,6 @@ use Behat\Behat\Context\Context; use Setono\SyliusCatalogPromotionPlugin\Command\ProcessPromotionsCommand; use Symfony\Bundle\FrameworkBundle\Console\Application; -use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Tester\CommandTester; use Symfony\Component\HttpKernel\KernelInterface; use Webmozart\Assert\Assert; diff --git a/tests/Behat/Context/Setup/CatalogPromotionContext.php b/tests/Behat/Context/Setup/CatalogPromotionContext.php index b0a4dc9..a820e0f 100644 --- a/tests/Behat/Context/Setup/CatalogPromotionContext.php +++ b/tests/Behat/Context/Setup/CatalogPromotionContext.php @@ -6,9 +6,8 @@ use Behat\Behat\Context\Context; use DateTime; -use Doctrine\Common\Persistence\ObjectManager; +use Doctrine\Persistence\ObjectManager; use Setono\SyliusCatalogPromotionPlugin\Factory\PromotionRuleFactoryInterface; -use Setono\SyliusCatalogPromotionPlugin\Model\Promotion; use Setono\SyliusCatalogPromotionPlugin\Model\PromotionInterface; use Setono\SyliusCatalogPromotionPlugin\Model\PromotionRuleInterface; use Setono\SyliusCatalogPromotionPlugin\Repository\PromotionRepositoryInterface; @@ -41,7 +40,7 @@ public function __construct( PromotionRuleFactoryInterface $promotionRuleFactory, TestPromotionFactoryInterface $testDiscountFactory, PromotionRepositoryInterface $promotionRepository, - ObjectManager $objectManager + ObjectManager $objectManager, ) { $this->sharedStorage = $sharedStorage; $this->promotionRuleFactory = $promotionRuleFactory; @@ -203,7 +202,7 @@ public function thisDiscountStartsTomorrow(PromotionInterface $promotion): void public function itGivesPercentageDiscount(PromotionInterface $promotion, float $percentage): void { $this->persistDiscount( - $this->setPercentageDiscount($promotion, $percentage) + $this->setPercentageDiscount($promotion, $percentage), ); } @@ -213,14 +212,14 @@ public function itGivesPercentageDiscount(PromotionInterface $promotion, float $ public function itGivesPercentageOffEveryProductClassifiedAs( PromotionInterface $promotion, float $percentage, - TaxonInterface $taxon + TaxonInterface $taxon, ): void { $this->createPercentageDiscount( $promotion, $percentage, $this->promotionRuleFactory->createHasTaxon([ $taxon->getCode(), - ]) + ]), ); } @@ -230,13 +229,13 @@ public function itGivesPercentageOffEveryProductClassifiedAs( public function itGivesOffOnEveryProductClassifiedAs( PromotionInterface $promotion, float $percentage, - array $promotionTaxons + array $promotionTaxons, ): void { $promotionTaxonsCodes = [$promotionTaxons[0]->getCode(), $promotionTaxons[1]->getCode()]; $this->createPercentageDiscount( $promotion, $percentage, - $this->promotionRuleFactory->createHasTaxon($promotionTaxonsCodes) + $this->promotionRuleFactory->createHasTaxon($promotionTaxonsCodes), ); } @@ -247,7 +246,7 @@ public function itGivesOffOnEveryProductClassifiedAs( public function itGivesPercentageDiscountOffOnAProduct( PromotionInterface $promotion, float $percentage, - ?ProductInterface $product = null + ?ProductInterface $product = null, ): void { if (null == $product) { $product = $this->sharedStorage->get('product'); @@ -256,7 +255,7 @@ public function itGivesPercentageDiscountOffOnAProduct( $this->createPercentageDiscount( $promotion, $percentage, - $this->promotionRuleFactory->createContainsProduct($product->getCode()) + $this->promotionRuleFactory->createContainsProduct($product->getCode()), ); } @@ -266,13 +265,13 @@ public function itGivesPercentageDiscountOffOnAProduct( public function itGivesPercentageDiscountOffOnAProducts( PromotionInterface $promotion, float $percentage, - array $products + array $products, ): void { $productCodes = [$products[0]->getCode(), $products[1]->getCode()]; $this->createPercentageDiscount( $promotion, $percentage, - $this->promotionRuleFactory->createContainsProducts($productCodes) + $this->promotionRuleFactory->createContainsProducts($productCodes), ); } @@ -302,11 +301,11 @@ public function theDiscountWasDisabledForTheChannel(PromotionInterface $promotio private function createPercentageDiscount( PromotionInterface $promotion, float $percentage, - PromotionRuleInterface $rule = null + PromotionRuleInterface $rule = null, ): void { $this->persistDiscount( $this->setPercentageDiscount($promotion, $percentage), - $rule + $rule, ); } diff --git a/tests/Behat/Context/Ui/Admin/ManagingPromotionsContext.php b/tests/Behat/Context/Ui/Admin/ManagingPromotionsContext.php index cb107cd..46c6157 100644 --- a/tests/Behat/Context/Ui/Admin/ManagingPromotionsContext.php +++ b/tests/Behat/Context/Ui/Admin/ManagingPromotionsContext.php @@ -43,7 +43,7 @@ public function __construct( CreatePageInterface $createPage, UpdatePageInterface $updatePage, CurrentPageResolverInterface $currentPageResolver, - NotificationCheckerInterface $notificationChecker + NotificationCheckerInterface $notificationChecker, ) { $this->sharedStorage = $sharedStorage; $this->indexPage = $indexPage; @@ -332,7 +332,7 @@ public function iShouldBeNotifiedOfFailure(): void { $this->notificationChecker->checkNotification( 'Cannot delete, the promotion is in use.', - NotificationType::failure() + NotificationType::failure(), ); } @@ -378,7 +378,7 @@ public function iShouldBeNotifiedThatThisValueShouldNotBeBlank(): void { Assert::same( $this->createPage->getValidationMessageForAction(), - 'This value should not be blank.' + 'This value should not be blank.', ); } @@ -403,7 +403,7 @@ public function iShouldSeeDiscountsOnTheList(int $count): void Assert::same( $count, $actualCount, - 'There should be %s promotion, but there\'s %2$s.' + 'There should be %s promotion, but there\'s %2$s.', ); } @@ -418,7 +418,7 @@ public function theFirstDiscountOnTheListShouldHave(string $field, string $value Assert::same( $actualValue, $value, - sprintf('Expected first promotion\'s %s to be "%s", but it is "%s".', $field, $value, $actualValue) + sprintf('Expected first promotion\'s %s to be "%s", but it is "%s".', $field, $value, $actualValue), ); } @@ -433,7 +433,7 @@ public function theLastDiscountOnTheListShouldHave(string $field, string $value) Assert::same( $actualValue, $value, - sprintf('Expected last promotion\'s %s to be "%s", but it is "%s".', $field, $value, $actualValue) + sprintf('Expected last promotion\'s %s to be "%s", but it is "%s".', $field, $value, $actualValue), ); } diff --git a/tests/Behat/Page/Admin/Promotion/CreatePage.php b/tests/Behat/Page/Admin/Promotion/CreatePage.php index 330e522..ad7bdab 100644 --- a/tests/Behat/Page/Admin/Promotion/CreatePage.php +++ b/tests/Behat/Page/Admin/Promotion/CreatePage.php @@ -17,11 +17,8 @@ class CreatePage extends BaseCreatePage implements CreatePageInterface { use NamesIt; - use SpecifiesItsCode; - use SpecifiesItsDiscount; - use PageDefinedElements; public function addRule(string $ruleName): void @@ -53,7 +50,7 @@ public function selectAutocompleteRuleOption(string $option, $value, bool $multi $element = $this ->getLastCollectionItem('rules') ->find('css', sprintf('input[type="hidden"][name*="[%s]"]', $option)) - ; + ; Assert::notNull($element); $ruleAutocomplete = $element->getParent(); diff --git a/tests/Behat/Page/Admin/Promotion/UpdatePage.php b/tests/Behat/Page/Admin/Promotion/UpdatePage.php index 30ec6f7..3ac47aa 100644 --- a/tests/Behat/Page/Admin/Promotion/UpdatePage.php +++ b/tests/Behat/Page/Admin/Promotion/UpdatePage.php @@ -14,11 +14,8 @@ class UpdatePage extends BaseUpdatePage implements UpdatePageInterface { use NamesIt; - use SpecifiesItsDiscount; - use ChecksCodeImmutability; - use PageDefinedElements; public function setPriority(?string $priority): void @@ -72,16 +69,16 @@ public function hasStartsAt(\DateTimeInterface $dateTime): bool { $timestamp = $dateTime->getTimestamp(); - return $this->getElement('starts_at_date')->getValue() === date('Y-m-d', $timestamp) - && $this->getElement('starts_at_time')->getValue() === date('H:i', $timestamp); + return $this->getElement('starts_at_date')->getValue() === date('Y-m-d', $timestamp) && + $this->getElement('starts_at_time')->getValue() === date('H:i', $timestamp); } public function hasEndsAt(\DateTimeInterface $dateTime): bool { $timestamp = $dateTime->getTimestamp(); - return $this->getElement('ends_at_date')->getValue() === date('Y-m-d', $timestamp) - && $this->getElement('ends_at_time')->getValue() === date('H:i', $timestamp); + return $this->getElement('ends_at_date')->getValue() === date('Y-m-d', $timestamp) && + $this->getElement('ends_at_time')->getValue() === date('H:i', $timestamp); } protected function getCodeElement(): NodeElement