diff --git a/.env.test b/.env.test
index 3261772..cfdfd53 100644
--- a/.env.test
+++ b/.env.test
@@ -2,6 +2,5 @@
KERNEL_CLASS='App\Kernel'
APP_SECRET='$ecretf0rt3st'
SYMFONY_DEPRECATIONS_HELPER=999999
-PANTHER_APP_ENV=panther
+PANTHER_APP_ENV=test
PANTHER_ERROR_SCREENSHOT_DIR=./var/error-screenshots
-DATABASE_URL="sqlite:///%kernel.project_dir%/var/data_tests.db"
diff --git a/.gitignore b/.gitignore
index 2f54f0e..d10c279 100644
--- a/.gitignore
+++ b/.gitignore
@@ -33,3 +33,6 @@ yarn-error.log
/phpunit.xml
.phpunit.result.cache
###< phpunit/phpunit ###
+
+# Tests
+/drivers/
diff --git a/.woodpecker.yml b/.woodpecker.yml
new file mode 100644
index 0000000..e388b8e
--- /dev/null
+++ b/.woodpecker.yml
@@ -0,0 +1,62 @@
+matrix:
+ PHP_VERSION:
+ # - 8.0
+ - 8.1
+
+services:
+ db:
+ image: mariadb:10.3
+ environment:
+ - MARIADB_ROOT_PASSWORD=root
+
+pipeline:
+ wait_db:
+ image: gitnet.fr/deblan/timeout:latest
+ commands:
+ - /bin/timeout -t 30 -v -c 'while true; do nc -z -v db 3306 2>&1 | grep succeeded && exit 0; sleep 0.5; done'
+
+ create_db:
+ image: mariadb:10.3
+ commands:
+ - mysql -hdb -uroot -proot -e "CREATE DATABASE app"
+ - mysql -hdb -uroot -proot -e "CREATE DATABASE app_test"
+
+ config:
+ image: deblan/php:8.1
+ commands:
+ - echo APP_ENV=prod >> .env.local
+ - echo APP_SECRET=$(openssl rand -hex 32) >> .env.local
+ - echo DATABASE_URL=mysql://root:root@db/app >> .env.local
+ - echo DATABASE_URL=mysql://root:root@db/app_test >> .env.test.local
+
+ composer:
+ image: deblan/php:${PHP_VERSION}
+ commands:
+ - apt-get update && apt-get -y install git
+ - composer install --no-scripts
+
+ migrate:
+ image: deblan/php:${PHP_VERSION}
+ environment:
+ - PHP=php
+ commands:
+ - ./bin/doctrine-migrate
+
+ node:
+ image: node:16-slim
+ commands:
+ - yarn
+ - test -d public/js || mkdir public/js
+ - test -f public/js/fos_js_routes.json || echo "{}" > public/js/fos_js_routes.json
+ - npm run build
+
+ tests:
+ image: deblan/php:${PHP_VERSION}
+ commands:
+ - apt-get update && apt-get install -y unzip
+ - composer install --no-scripts --dev
+ - curl -o chromedriver_linux64.zip https://chromedriver.storage.googleapis.com/108.0.5359.71/chromedriver_linux64.zip && unzip -d drivers chromedriver_linux64.zip
+ - curl -o /tmp/chrome.deb https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb && apt install -y /tmp/chrome.deb
+ - vendor/bin/bdi detect drivers
+ - symfony server:start --port=9080 --no-tls -d
+ - php bin/phpunit
diff --git a/Makefile b/Makefile
index 4a190b7..79f7f65 100644
--- a/Makefile
+++ b/Makefile
@@ -24,3 +24,6 @@ doctrine-migration:
PHP=$(PHP_BIN) ./bin/doctrine-migrate
build: clean js-routing asset
+
+run-tests:
+ $(PHP_BIN) bin/phpunit tests/
diff --git a/bin/doctrine-migrate b/bin/doctrine-migrate
index 42a19a0..1125cd8 100755
--- a/bin/doctrine-migrate
+++ b/bin/doctrine-migrate
@@ -1,7 +1,9 @@
#!/bin/sh
-CLASS_NAME="$(echo "yes" | "$PHP" ./bin/console doctrine:migration:diff -e dev | grep -o "Version[0-9]*" | tail -n 1)"
+APP_ENV="${APP_ENV:-dev}"
+
+CLASS_NAME="$(echo "yes" | "$PHP" ./bin/console doctrine:migration:diff -e "$APP_ENV" | grep -o "Version[0-9]*" | tail -n 1)"
if [ -n "$CLASS_NAME" ]; then
- echo "yes" | "$PHP" ./bin/console doctrine:migration:exec --up "DoctrineMigrations\\$CLASS_NAME" -e dev
+ echo "yes" | "$PHP" ./bin/console doctrine:migration:exec --up "DoctrineMigrations\\$CLASS_NAME" -e "$APP_ENV"
fi
diff --git a/composer.json b/composer.json
index dea6ca6..28f9f87 100644
--- a/composer.json
+++ b/composer.json
@@ -7,13 +7,15 @@
"prefer-stable": true,
"require": {
"php": ">=8.0.0",
- "murph/murph-core": "^1.17"
+ "murph/murph-core": "dev-master"
},
"require-dev": {
+ "dbrekelmans/bdi": "^1.0",
"symfony/browser-kit": "^5.4",
"symfony/css-selector": "^5.4",
"symfony/debug-bundle": "^5.4",
"symfony/maker-bundle": "^1.0",
+ "symfony/panther": "^2.0",
"symfony/phpunit-bridge": "^6.2",
"symfony/stopwatch": "^5.4",
"symfony/var-dumper": "^5.4",
diff --git a/config/packages/doctrine.yaml b/config/packages/doctrine.yaml
index b07e18f..0d4e65a 100644
--- a/config/packages/doctrine.yaml
+++ b/config/packages/doctrine.yaml
@@ -29,11 +29,11 @@ doctrine:
alias: GedmoTree # (optional) it will default to the name set for the mapping
is_bundle: false
-when@test:
- doctrine:
- dbal:
- # "TEST_TOKEN" is typically set by ParaTest
- dbname_suffix: '_test%env(default::TEST_TOKEN)%'
+# when@test:
+# doctrine:
+# dbal:
+# # "TEST_TOKEN" is typically set by ParaTest
+# dbname_suffix: '_test%env(default::TEST_TOKEN)%'
when@prod:
doctrine:
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
index af3f147..96e8ed1 100644
--- a/phpunit.xml.dist
+++ b/phpunit.xml.dist
@@ -13,13 +13,22 @@
+
-
- tests
+
+ tests/Core/Command/CreateUserTest.php
+
+
+ tests/Core/Auth/LoginTest.php
+
+
+ tests/Core/Site/NavigationTest.php
+ tests/Core/Site/TreeTest.php
+ tests/Core/Site/PageTest.php
@@ -33,10 +42,7 @@
-
-
diff --git a/tests/Core/Auth/LoginTest.php b/tests/Core/Auth/LoginTest.php
new file mode 100644
index 0000000..2b8c96e
--- /dev/null
+++ b/tests/Core/Auth/LoginTest.php
@@ -0,0 +1,40 @@
+client = static::createClient();
+ $this->query = self::$container->get(UserRepositoryQuery::class);
+ }
+
+ public function testLoginRedirect(): void
+ {
+ $crawler = $this->client->request('GET', '/admin');
+
+ $this->assertResponseStatusCodeSame(302);
+ $this->client->followRedirect();
+ $this->assertResponseIsSuccessful();
+ }
+
+ public function testLoginUser(): void
+ {
+ $user = $this->query->create()->andWhere('.email=\'admin@localhost\'')->findOne();
+ $this->client->loginUser($user);
+ $this->client->request('GET', '/admin/account/');
+ $this->assertResponseStatusCodeSame(200);
+ }
+}
diff --git a/tests/Core/Command/CreateUserTest.php b/tests/Core/Command/CreateUserTest.php
new file mode 100644
index 0000000..63af056
--- /dev/null
+++ b/tests/Core/Command/CreateUserTest.php
@@ -0,0 +1,68 @@
+query = self::$container->get(UserRepositoryQuery::class);
+ }
+
+ public function testCommandExecute(): void
+ {
+ $kernel = static::createKernel();
+ $application = new Application($kernel);
+ $command = $application->find('murph:user:create');
+ $commandTester = new CommandTester($command);
+
+ $commandTester->setInputs([
+ 'admin@localhost',
+ 'admin_password',
+ 'y',
+ 'n',
+ ]);
+ $commandTester->execute(['command' => $command->getName()]);
+ $output = $commandTester->getDisplay();
+ $this->assertStringContainsString('User created!', $output);
+
+ $commandTester->setInputs([
+ 'writer@localhost',
+ 'writer_password',
+ 'n',
+ 'y',
+ ]);
+ $commandTester->execute(['command' => $command->getName()]);
+ $output = $commandTester->getDisplay();
+ $this->assertStringContainsString('User created!', $output);
+ }
+
+ public function testCreatedUsers(): void
+ {
+ $users = $this->query->create()->find();
+ $this->assertEquals(2, count($users));
+
+ $this->assertEquals('admin@localhost', $users[0]->getEmail());
+ $this->assertEquals('admin@localhost', $users[0]->getUsername());
+ $this->assertEquals('writer@localhost', $users[1]->getEmail());
+ $this->assertEquals('writer@localhost', $users[1]->getUsername());
+
+ $this->assertEquals(true, $users[0]->getIsAdmin());
+ $this->assertEquals(false, $users[0]->getIsWriter());
+ $this->assertEquals(false, $users[1]->getIsAdmin());
+ $this->assertEquals(true, $users[1]->getIsWriter());
+ }
+}
diff --git a/tests/Core/PantherTestCase.php b/tests/Core/PantherTestCase.php
new file mode 100644
index 0000000..f81c358
--- /dev/null
+++ b/tests/Core/PantherTestCase.php
@@ -0,0 +1,50 @@
+client = static::createPantherClient([
+ 'external_base_uri' => 'http://localhost:9080'
+ ]);
+ $this->query = $this->container()->get(UserRepositoryQuery::class);
+ }
+
+ protected function authenticateAdmin(): void
+ {
+ $this->client->request('GET', '/login');
+ $this->client->submitForm('Login', [
+ '_username' => 'admin@localhost',
+ '_password' => 'admin_password',
+ ]);
+ $this->client->waitFor('.nav-item-label');
+ }
+
+ protected function authenticateWriter(): void
+ {
+ $this->client->request('GET', '/login');
+ $this->client->submitForm('Login', [
+ '_username' => 'writer@localhost',
+ '_password' => 'writer_password',
+ ]);
+ $this->client->waitFor('.nav-item-label');
+ }
+}
diff --git a/tests/Core/Site/NavigationTest.php b/tests/Core/Site/NavigationTest.php
new file mode 100644
index 0000000..1108a13
--- /dev/null
+++ b/tests/Core/Site/NavigationTest.php
@@ -0,0 +1,39 @@
+authenticateAdmin();
+
+ $this->client->request('GET', '/admin/site/tree');
+ $this->client->waitFor('.toast-body.text-text-warning');
+ $this->assertSelectorTextContains('.toast-body.text-text-warning', 'You must add a navigation.');
+
+ $this->client->request('GET', '/admin/site/navigation');
+ $this->assertSelectorTextContains('h1', 'Navigations');
+
+ $this->client->request('GET', '/admin/site/navigation/new');
+ $this->assertSelectorTextContains('h1', 'New navigation');
+ $this->client->submitForm('Save', [
+ 'navigation[label]' => 'Test navigation',
+ 'navigation[locale]' => 'en',
+ 'navigation[code]' => 'nav',
+ 'navigation[domain]' => 'localhost',
+ ]);
+
+ $this->client->waitFor('.toast-body.text-text-success');
+ $this->assertSelectorTextContains('.toast-body.text-text-success', 'The data has been saved.');
+
+ $this->client->request('GET', '/admin/site/navigation');
+ $this->assertSelectorTextContains('.table tbody tr td', 'Test navigation');
+ }
+}
diff --git a/tests/Core/Site/PageTest.php b/tests/Core/Site/PageTest.php
new file mode 100644
index 0000000..0a7a529
--- /dev/null
+++ b/tests/Core/Site/PageTest.php
@@ -0,0 +1,30 @@
+client->request('GET', '/admin/site/tree');
+ $this->client->executeScript("document.querySelector('#node-2 .float-right button[data-modal]').click()");
+
+ $this->client->waitFor('#form-node-edit');
+ $this->client->executeScript("document.querySelector('#node-page-action .card-header label').click()");
+ $this->client->executeScript("document.querySelector('a[href=\"#form-node-edit-routing\"]').click()");
+ $this->client->executeScript("document.querySelector('#node_url').value='/foo'");
+ $this->client->executeScript("document.querySelector('#node_code').value='/foo'");
+ $this->client->executeScript("document.querySelector('.modal.show .modal-footer button[type=\"submit\"]').click()");
+
+ $this->client->waitFor('.toast-body.text-text-success');
+ $this->assertSelectorTextContains('.toast-body.text-text-success', 'The data has been saved.');
+
+ $this->assertSelectorTextContains('#node-2 .float-right a', 'Page');
+ }
+}
diff --git a/tests/Core/Site/TreeTest.php b/tests/Core/Site/TreeTest.php
new file mode 100644
index 0000000..d51d114
--- /dev/null
+++ b/tests/Core/Site/TreeTest.php
@@ -0,0 +1,32 @@
+client->request('GET', '/admin/site/tree');
+ $this->assertSelectorTextContains('button[data-toggle="modal"]', 'Add a menu');
+ $this->client->executeScript("document.querySelector('button[data-toggle=\"modal\"]').click()");
+
+ $this->client->waitFor('#form-menu-new');
+ $this->client->submitForm('Save', [
+ 'menu[label]' => 'Test menu',
+ 'menu[code]' => 'menu',
+ ]);
+
+ $this->client->waitFor('.toast-body.text-text-success');
+ $this->assertSelectorTextContains('.toast-body.text-text-success', 'The data has been saved.');
+
+ $this->client->request('GET', '/admin/site/tree');
+ $this->assertSelectorTextContains('.h4', 'Test menu');
+ $this->assertSelectorTextContains('#node-2 .col-6', 'First element');
+ }
+}
diff --git a/tests/bootstrap.php b/tests/bootstrap.php
index 3181151..1ff1e4c 100644
--- a/tests/bootstrap.php
+++ b/tests/bootstrap.php
@@ -13,3 +13,9 @@ if (file_exists(dirname(__DIR__).'/config/bootstrap.php')) {
if ($_SERVER['APP_DEBUG']) {
umask(0000);
}
+
+passthru(sprintf(
+ 'APP_ENV=test PHP=%s "%s/../bin/doctrine-migrate"',
+ $_ENV['PHP_BIN'] ?? $_ENV['PHP'] ?? 'php',
+ __DIR__
+));