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__ +));