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 = ''