diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 95e49a6..7fc4df2 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -99,3 +99,12 @@ jobs: - name: Run Unit Tests PHP8.3 run: nix-shell --arg phpVersion \"php83\" --pure --run project-test-unit + + - name: Run Acceptance Tests PHP8.1 + run: nix-shell --arg phpVersion \"php81\" --pure --run project-test-acceptance + + - name: Run Acceptance Tests PHP8.2 + run: nix-shell --arg phpVersion \"php82\" --pure --run project-test-acceptance + + - name: Run Acceptance Tests PHP8.3 + run: nix-shell --arg phpVersion \"php83\" --pure --run project-test-acceptance diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 37c00a7..ca69da6 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -139,6 +139,58 @@ test:php83:typo3_12: TYPO3_VERSION: ^12.4 COVERAGE: 1 +.test_codeception: &test_codeception + stage: test + image: $CONTAINER_IMAGE + only: + - branches + before_script: + - sed -i -e "s#ssh://git@code.extco.de:22722#https://gitlab-ci-token:$CI_JOB_TOKEN@code.extco.de#g" composer.json + - composer config --no-plugins allow-plugins.typo3/cms-composer-installers true + - composer config --no-plugins allow-plugins.typo3/class-alias-loader true + - composer require typo3/cms-core="${TYPO3_VERSION}" + script: + - mkdir -p .build/public/typo3temp/var/tests/acceptance-sqlite-dbs + - export typo3DatabaseDriver=pdo_sqlite + - export PROJECT_ROOT="$(pwd)" + - export INSTANCE_PATH="$(pwd)/.build/web/typo3temp/var/tests/acceptance" + - mkdir -p "$INSTANCE_PATH" + - mkdir -p "$PROJECT_ROOT/.build/web/typo3temp/var/tests/acceptance-logs/" + - vendor/bin/codecept run + artifacts: + paths: + - .build + expire_in: 1 day + when: always + +# Build in PHP 8.1 and TYPO3 12.4 +test:codception:php81:typo3_12: + <<: *test_codeception + variables: + CONTAINER_IMAGE: $CI_REGISTRY/containers/codeception-with-php-8.1:main + TYPO3_VERSION: ^12.4 + GECKODRIVER_VERSION: v0.34.0 + +# Build in PHP 8.2 and TYPO3 12.4 +test:codception:php82:typo3_12: + <<: *test_codeception + needs: + - test:codception:php81:typo3_12 + variables: + CONTAINER_IMAGE: $CI_REGISTRY/containers/codeception-with-php-8.2:main + TYPO3_VERSION: ^12.4 + GECKODRIVER_VERSION: v0.34.0 + +# Build in PHP 8.3 and TYPO3 12.4 +test:codception:php83:typo3_12: + <<: *test_codeception + needs: + - test:codception:php82:typo3_12 + variables: + CONTAINER_IMAGE: $CI_REGISTRY/containers/codeception-with-php-8.3:main + TYPO3_VERSION: ^12.4 + GECKODRIVER_VERSION: v0.34.0 + documentation: stage: documentation image: diff --git a/Tests/Acceptance/Data/.gitkeep b/Tests/Acceptance/Data/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/Tests/Acceptance/EventListCest.php b/Tests/Acceptance/EventListCest.php new file mode 100644 index 0000000..5c53fcf --- /dev/null +++ b/Tests/Acceptance/EventListCest.php @@ -0,0 +1,50 @@ +amOnUrl('http://127.0.0.1:8080/events/'); + + $I->see('Event 1'); + $I->see('Teaser 1'); + + $I->dontSee('Event 4'); + + $I->click('Event 1'); + $I->see('Event 1', 'h1'); + $I->see('31.07.2024 10:00'); + $I->see('This event date can not be booked.'); + } + + public function testListAndDetailViewForBookableEventWithoutPriceCategories(Tester $I): void + { + $I->amOnUrl('http://127.0.0.1:8080/events/'); + + $I->see('Event 2'); + $I->see('Teaser 2'); + + $I->dontSee('Event 4'); + + $I->click('Event 2'); + $I->see('Event 2', 'h1'); + $I->see('31.07.2024 10:00'); + + $I->dontSee('This event date can not be booked.'); + $I->seeElement("input[name='tx_cart_cart[quantity]']"); + $I->seeElement('input[type="submit"]'); + } +} diff --git a/Tests/Acceptance/Support/Environment.php b/Tests/Acceptance/Support/Environment.php new file mode 100644 index 0000000..3641f93 --- /dev/null +++ b/Tests/Acceptance/Support/Environment.php @@ -0,0 +1,60 @@ + [ + 'typo3/cms-core', + 'typo3/cms-backend', + 'typo3/cms-extbase', + 'typo3/cms-frontend', + 'typo3/cms-fluid', + 'typo3/cms-fluid-styled-content', + 'typo3/cms-install', + ], + 'testExtensionsToLoad' => [ + 'extcode/cart', + 'extcode/cart-events', + __DIR__ . '/../../Fixtures/cart_events_test', + ], + 'phpDatabaseFixtures' => [ + 'typo3conf/ext/cart_events/Tests/Fixtures/BackendUserDatabase.php', + 'typo3conf/ext/cart_events/Tests/Fixtures/PagesDatabase.php', + 'typo3conf/ext/cart_events/Tests/Fixtures/ContentDatabase.php', + 'typo3conf/ext/cart_events/Tests/Fixtures/EventsDatabase.php', + ], + 'pathsToLinkInTestInstance' => [ + 'typo3conf/ext/cart_events/Tests/Fixtures/config/sites' => 'typo3conf/sites', + ], + 'configurationToUseInTestInstance' => [ + 'SYS' => [ + 'trustedHostsPattern' => '.*', + ], + ], + ]; + + public function bootstrapTypo3Environment(SuiteEvent $suiteEvent): void + { + parent::bootstrapTypo3Environment($suiteEvent); + + assert(is_array($this->config['phpDatabaseFixtures'])); + foreach ($this->config['phpDatabaseFixtures'] as $dataSetFile) { + (new PhpDataSet())->import(include $dataSetFile); + } + } +} diff --git a/Tests/Acceptance/Support/Tester.php b/Tests/Acceptance/Support/Tester.php new file mode 100644 index 0000000..d0f5dd6 --- /dev/null +++ b/Tests/Acceptance/Support/Tester.php @@ -0,0 +1,37 @@ + [ + [ + 'uid' => '1', + 'pid' => '0', + 'username' => 'test-admin', + 'password' => '$2y$12$ksKbFwhPyf9EWWM52R2e4ubG09tPTK9W6139nxpsgwyUHW17W1nye', + 'lang' => 'default', + 'admin' => '1', + ], + ], +]; diff --git a/Tests/Fixtures/ContentDatabase.php b/Tests/Fixtures/ContentDatabase.php new file mode 100644 index 0000000..a4950e2 --- /dev/null +++ b/Tests/Fixtures/ContentDatabase.php @@ -0,0 +1,24 @@ + [ + 0 => [ + 'uid' => '1', + 'pid' => '3', + 'CType' => 'list', + 'list_type' => 'cartevents_events', + 'pages' => '7', + 'pi_flexform' => ' Event->show;Event->list table 0 ', + ], + 1 => [ + 'uid' => '2', + 'pid' => '11', + 'CType' => 'list', + 'list_type' => 'cart_cart', + 'pages' => '', + 'pi_flexform' => '', + ], + ], +]; diff --git a/Tests/Fixtures/EventsDatabase.php b/Tests/Fixtures/EventsDatabase.php new file mode 100644 index 0000000..cc91766 --- /dev/null +++ b/Tests/Fixtures/EventsDatabase.php @@ -0,0 +1,106 @@ + [ + 0 => [ + 'uid' => '1', + 'pid' => '7', + 'sku' => 'event-1', + 'title' => 'Event 1', + 'teaser' => 'Teaser 1', + 'description' => '', + 'meta_description' => '', + 'audience' => '', + 'path_segment' => 'event-1', + ], + 1 => [ + 'uid' => '2', + 'pid' => '7', + 'sku' => 'event-2', + 'title' => 'Event 2', + 'teaser' => 'Teaser 2', + 'description' => '', + 'meta_description' => '', + 'audience' => '', + 'path_segment' => 'event-2', + ], + 2 => [ + 'uid' => '3', + 'pid' => '7', + 'sku' => 'event-3', + 'title' => 'Event 3', + 'teaser' => 'Teaser 3', + 'description' => '', + 'meta_description' => '', + 'audience' => '', + 'path_segment' => 'event-3', + ], + 3 => [ + 'uid' => '4', + 'pid' => '9', + 'sku' => 'event-4', + 'title' => 'Event 4', + 'teaser' => '', + 'description' => '', + 'meta_description' => '', + 'audience' => '', + 'path_segment' => 'event-4', + ], + ], + 'tx_cartevents_domain_model_eventdate' => [ + 0 => [ + 'uid' => '1', + 'pid' => '7', + 'event' => '1', + 'sku' => 'eventdate-1', + 'title' => 'Eventdate 1', + 'begin' => '1722420000', + 'location' => '', + 'lecturer' => '', + 'note' => '', + 'price' => 9.99, + 'bookable' => false, + ], + 1 => [ + 'uid' => '2', + 'pid' => '7', + 'event' => '2', + 'sku' => 'eventdate-2', + 'title' => 'Eventdate 2', + 'begin' => '1722420000', + 'location' => '', + 'lecturer' => '', + 'note' => '', + 'price' => 19.99, + 'bookable' => true, + ], + 2 => [ + 'uid' => '3', + 'pid' => '7', + 'event' => '3', + 'sku' => 'eventdate-3', + 'title' => 'Eventdate 3', + 'begin' => '1722420000', + 'location' => '', + 'lecturer' => '', + 'note' => '', + 'price' => 29.99, + 'bookable' => true, + ], + 3 => [ + 'uid' => '4', + 'pid' => '9', + 'event' => '4', + 'sku' => 'eventdate-4', + 'title' => 'Eventdate 4', + 'begin' => '1722420000', + 'location' => '', + 'lecturer' => '', + 'note' => '', + 'price' => 9.99, + 'bookable' => true, + ], + ], +]; diff --git a/Tests/Fixtures/PagesDatabase.php b/Tests/Fixtures/PagesDatabase.php new file mode 100644 index 0000000..13c7072 --- /dev/null +++ b/Tests/Fixtures/PagesDatabase.php @@ -0,0 +1,167 @@ + [ + 0 => [ + 'uid' => '1', + 'pid' => '0', + 'title' => 'Home', + 'doktype' => PageRepository::DOKTYPE_DEFAULT, + 'slug' => '/', + 'sorting' => '128', + 'deleted' => '0', + 'is_siteroot' => '1', + ], + 1 => [ + 'uid' => '2', + 'pid' => '0', + 'title' => 'Startseite', + 'doktype' => PageRepository::DOKTYPE_DEFAULT, + 'slug' => '/', + 'sorting' => '128', + 'deleted' => '0', + 'is_siteroot' => '1', + 'sys_language_uid' => 2, + 'l10n_parent' => 1, + 'l10n_source' => 1, + ], + 2 => [ + 'uid' => '3', + 'pid' => '1', + 'title' => 'Events', + 'doktype' => PageRepository::DOKTYPE_DEFAULT, + 'slug' => '/events', + 'sorting' => '128', + 'deleted' => '0', + ], + 3 => [ + 'uid' => '4', + 'pid' => '1', + 'title' => 'Veranstaltungen', + 'doktype' => PageRepository::DOKTYPE_DEFAULT, + 'slug' => '/veranstaltungen', + 'sorting' => '128', + 'deleted' => '0', + 'sys_language_uid' => 2, + 'l10n_parent' => 3, + 'l10n_source' => 3, + ], + 4 => [ + 'uid' => '5', + 'pid' => '1', + 'title' => 'Shop', + 'doktype' => PageRepository::DOKTYPE_SYSFOLDER, + 'slug' => '/shop', + 'sorting' => '128', + 'deleted' => '0', + ], + 5 => [ + 'uid' => '6', + 'pid' => '1', + 'title' => 'Shop', + 'doktype' => PageRepository::DOKTYPE_SYSFOLDER, + 'slug' => '/shop', + 'sorting' => '128', + 'deleted' => '0', + 'sys_language_uid' => 2, + 'l10n_parent' => 5, + 'l10n_source' => 5, + ], + 6 => [ + 'uid' => '7', + 'pid' => '5', + 'title' => 'Events Folder 1', + 'doktype' => PageRepository::DOKTYPE_SYSFOLDER, + 'slug' => '/events-folder-1', + 'sorting' => '128', + 'deleted' => '0', + ], + 7 => [ + 'uid' => '8', + 'pid' => '5', + 'title' => 'Veranstaltungsordner 1', + 'doktype' => PageRepository::DOKTYPE_SYSFOLDER, + 'slug' => '/veranstaltungsordner-1', + 'sorting' => '128', + 'deleted' => '0', + 'sys_language_uid' => 2, + 'l10n_parent' => 7, + 'l10n_source' => 7, + ], + 8 => [ + 'uid' => '9', + 'pid' => '5', + 'title' => 'Events Folder 2', + 'doktype' => PageRepository::DOKTYPE_SYSFOLDER, + 'slug' => '/events-folder-2', + 'sorting' => '128', + 'deleted' => '0', + ], + 9 => [ + 'uid' => '10', + 'pid' => '5', + 'title' => 'Veranstaltungsordner 2', + 'doktype' => PageRepository::DOKTYPE_SYSFOLDER, + 'slug' => '/eranstaltungsordner-2', + 'sorting' => '128', + 'deleted' => '0', + 'sys_language_uid' => 2, + 'l10n_parent' => 9, + 'l10n_source' => 9, + ], + 10 => [ + 'uid' => '11', + 'pid' => '1', + 'title' => 'Cart', + 'doktype' => PageRepository::DOKTYPE_DEFAULT, + 'slug' => '/cart', + 'sorting' => '128', + 'deleted' => '0', + ], + 11 => [ + 'uid' => '12', + 'pid' => '1', + 'title' => 'Warenkorb', + 'doktype' => PageRepository::DOKTYPE_DEFAULT, + 'slug' => '/warenkorb', + 'sorting' => '128', + 'deleted' => '0', + 'sys_language_uid' => 2, + 'l10n_parent' => 11, + 'l10n_source' => 11, + ], + 12 => [ + 'uid' => '13', + 'pid' => '5', + 'title' => 'Orders', + 'doktype' => PageRepository::DOKTYPE_SYSFOLDER, + 'slug' => '/orders', + 'sorting' => '128', + 'deleted' => '0', + ], + 13 => [ + 'uid' => '14', + 'pid' => '5', + 'title' => 'Bestellungen', + 'doktype' => PageRepository::DOKTYPE_SYSFOLDER, + 'slug' => '/bestellungen', + 'sorting' => '128', + 'deleted' => '0', + 'sys_language_uid' => 2, + 'l10n_parent' => 13, + 'l10n_source' => 13, + ], + ], + 'sys_template' => [ + 0 => [ + 'uid' => '1', + 'pid' => '1', + 'title' => 'Test Template', + 'include_static_file' => 'EXT:fluid_styled_content/Configuration/TypoScript/,EXT:cart_events_test/Configuration/TypoScript,EXT:cart/Configuration/TypoScript,EXT:cart_events/Configuration/TypoScript', + ], + ], +]; diff --git a/Tests/Fixtures/cart_events_test/Configuration/TypoScript/setup.typoscript b/Tests/Fixtures/cart_events_test/Configuration/TypoScript/setup.typoscript new file mode 100644 index 0000000..2529a43 --- /dev/null +++ b/Tests/Fixtures/cart_events_test/Configuration/TypoScript/setup.typoscript @@ -0,0 +1,7 @@ +page = PAGE +page.10 < styles.content.get + +plugin.tx_cart.settings { + cart.pid = 11 + order.pid = 13 +} \ No newline at end of file diff --git a/Tests/Fixtures/cart_events_test/composer.json b/Tests/Fixtures/cart_events_test/composer.json new file mode 100644 index 0000000..2b59475 --- /dev/null +++ b/Tests/Fixtures/cart_events_test/composer.json @@ -0,0 +1,15 @@ +{ + "name": "extcode/cart-events-test", + "description": "Add a cart_events test extension", + "type": "typo3-cms-extension", + "license": "GPL-2.0-or-later", + "require": { + "typo3/cms-core": "*", + "extcode/cart-events": "@dev" + }, + "extra": { + "typo3/cms": { + "extension-key": "cart_events_test" + } + } +} \ No newline at end of file diff --git a/Tests/Fixtures/config/sites/default/config.yaml b/Tests/Fixtures/config/sites/default/config.yaml new file mode 100644 index 0000000..90ed27e --- /dev/null +++ b/Tests/Fixtures/config/sites/default/config.yaml @@ -0,0 +1,34 @@ +base: '/' +languages: + - + title: 'English' + enabled: true + base: '/' + typo3Language: 'default' + locale: 'en_US.UTF-8' + iso-639-1: 'en' + websiteTitle: '' + navigationTitle: 'English' + hreflang: 'en-GB' + direction: '' + flag: 'gb' + languageId: 0 + fallbackType: 'strict' + fallbacks: '0' + - + title: 'Deutsch' + enabled: true + base: '/de/' + typo3Language: 'de' + locale: 'de_DE.UTF-8' + iso-639-1: 'de' + navigationTitle: 'Deutsch' + hreflang: 'de-DE' + direction: 'ltr' + fallbackType: 'strict' + fallbacks: '' + flag: 'de' + languageId: 2 + websiteTitle: '' +rootPageId: 1 +websiteTitle: 'Cart Products Test - default' \ No newline at end of file diff --git a/codeception.dist.yml b/codeception.dist.yml new file mode 100644 index 0000000..9808a21 --- /dev/null +++ b/codeception.dist.yml @@ -0,0 +1,45 @@ +namespace: 'Extcode\CartEvents\Tests\Acceptance\Support' + +paths: + tests: 'Tests/Acceptance' + data: 'Tests/Acceptance/Data' + output: '.build/web/typo3temp/var/tests/acceptance-reports' + support: 'Tests/Acceptance/Support' + +settings: + debug: true + +extensions: + enabled: + - + 'Codeception\Extension\RunProcess': + - 'geckodriver > .build/web/typo3temp/var/tests/acceptance-logs/geckodriver.log 2>&1' + - 'TYPO3_PATH_APP="$INSTANCE_PATH" TYPO3_PATH_ROOT="$INSTANCE_PATH" php -S 127.0.0.1:8080 -t "$INSTANCE_PATH" > .build/web/typo3temp/var/tests/acceptance-logs/php.log 2>&1' + - + 'Codeception\Extension\Recorder' + - + 'Extcode\CartEvents\Tests\Acceptance\Support\Environment': + typo3DatabaseDriver: 'pdo_sqlite' + +suites: + acceptance: + actor: 'Tester' + path: '.' + modules: + enabled: + - + WebDriver: + url: 'http://127.0.0.1:8080/' + browser: 'firefox' + restart: true + path: '' + wait: 5 + # Scrolling within iFrame doesn't work so well, so we use a bigger window size. + window_size: '1920x1080' + capabilities: + moz:firefoxOptions: + args: + - '-headless' + + step_decorators: + - 'Codeception\Step\Retry' diff --git a/composer.json b/composer.json index fbb6fae..0f33a89 100644 --- a/composer.json +++ b/composer.json @@ -55,6 +55,10 @@ "typo3/cms-fluid": "^12.4" }, "require-dev": { + "codappix/typo3-php-datasets": "^1.5", + "codeception/codeception": "^5.0", + "codeception/module-db": "^3.1", + "codeception/module-webdriver": "^4.0", "friendsofphp/php-cs-fixer": "^3.16", "helmich/typo3-typoscript-lint": "^3.1", "overtrue/phplint": "^5.5", diff --git a/shell.nix b/shell.nix index debc117..3b54c6c 100644 --- a/shell.nix +++ b/shell.nix @@ -76,6 +76,32 @@ let ''; }; + projectTestAcceptance = pkgs.writeShellApplication { + name = "project-test-acceptance"; + runtimeInputs = [ + projectInstall + pkgs.sqlite + pkgs.firefox + pkgs.geckodriver + pkgs.procps + php + ]; + text = '' + project-install + + mkdir -p "$PROJECT_ROOT/.build/web/typo3temp/var/tests/acceptance" + mkdir -p "$PROJECT_ROOT/.build/web/typo3temp/var/tests/acceptance-logs" + mkdir -p "$PROJECT_ROOT/.build/web/typo3temp/var/tests/acceptance-reports" + mkdir -p "$PROJECT_ROOT/.build/web/typo3temp/var/tests/acceptance-sqlite-dbs" + + export INSTANCE_PATH="$PROJECT_ROOT/.build/web/typo3temp/var/tests/acceptance" + + ./vendor/bin/codecept run + + pgrep -f "php -S" | xargs -r kill + ''; + }; + in pkgs.mkShellNoCC { name = "TYPO3 Extension extcode/cart-products"; buildInputs = [ @@ -86,6 +112,7 @@ in pkgs.mkShellNoCC { projectCgl projectCglFix projectTestUnit + projectTestAcceptance ]; shellHook = ''