diff --git a/.env.ci b/.env.ci new file mode 100644 index 0000000..dd6472b --- /dev/null +++ b/.env.ci @@ -0,0 +1,23 @@ +APP_NAME=Laravel +APP_ENV=testing +APP_KEY=base64:4Hjdxw6l/vsqukgRVRk5YwKHNJfX9N+44dJdnDesrcc= +APP_DEBUG=false +APP_URL=http://127.0.0.1 + +LOG_CHANNEL=stack +LOG_LEVEL=debug + +DB_CONNECTION=mysql +DB_HOST=127.0.0.1 +DB_PORT=3306 +DB_DATABASE=toby +DB_USERNAME=toby +DB_PASSWORD=password + +BROADCAST_DRIVER=log +CACHE_DRIVER=array +QUEUE_CONNECTION=sync +SESSION_DRIVER=array +SESSION_LIFETIME=120 +FILESYSTEM_DRIVER=local +MAIL_MAILER=array diff --git a/.env.dusk.local b/.env.dusk.local new file mode 100644 index 0000000..c68513c --- /dev/null +++ b/.env.dusk.local @@ -0,0 +1,28 @@ +APP_NAME=Laravel +APP_ENV=testing +APP_KEY=base64:uuizKfYGhrBB+ecWuxg570hdUgKwZ1sqEgHPngW15Xw= +APP_DEBUG=false +APP_URL=http://toby-web + +DUSK_IN_DOCKER=true +DUSK_DRIVER_URL=http://toby-selenium:4444/wd/hub + +LOG_CHANNEL=stack +LOG_LEVEL=debug + +DB_CONNECTION=mysql +DB_HOST=toby-db-test +DB_PORT=3306 +DB_DATABASE=toby +DB_USERNAME=toby +DB_PASSWORD=password + +BROADCAST_DRIVER=log +CACHE_DRIVER=array +QUEUE_CONNECTION=sync +SESSION_DRIVER=file +SESSION_LIFETIME=120 +FILESYSTEM_DRIVER=local +MAIL_MAILER=array + +TELESCOPE_ENABLED=false diff --git a/.env.example b/.env.example index 30e2550..b4d8f7a 100644 --- a/.env.example +++ b/.env.example @@ -8,18 +8,24 @@ LOG_CHANNEL=stack LOG_LEVEL=debug DB_CONNECTION=mysql -DB_HOST=toby-db +DB_HOST=toby-db-dev DB_PORT=3306 DB_DATABASE=toby DB_USERNAME=toby DB_PASSWORD=password DB_ROOT_PASSWORD=example -DOCKER_DB_EXTERNAL_PORT=3306 -DOCKER_DB_DATABASE=${DB_DATABASE} -DOCKER_DB_USERNAME=${DB_USERNAME} -DOCKER_DB_PASSWORD=${DB_PASSWORD} -DOCKER_DB_ROOT_PASSWORD=${DB_ROOT_PASSWORD} +DOCKER_DEV_DB_EXTERNAL_PORT=3306 +DOCKER_DEV_DB_DATABASE=${DB_DATABASE} +DOCKER_DEV_DB_USERNAME=${DB_USERNAME} +DOCKER_DEV_DB_PASSWORD=${DB_PASSWORD} +DOCKER_DEV_DB_ROOT_PASSWORD=${DB_ROOT_PASSWORD} + +DOCKER_TEST_DB_EXTERNAL_PORT=3307 +DOCKER_TEST_DB_DATABASE=${DB_DATABASE} +DOCKER_TEST_DB_USERNAME=${DB_USERNAME} +DOCKER_TEST_DB_PASSWORD=${DB_PASSWORD} +DOCKER_TEST_DB_ROOT_PASSWORD=${DB_ROOT_PASSWORD} EXTERNAL_WEBSERVER_PORT= XDG_CONFIG_HOME=/tmp diff --git a/.github/workflows/test-and-lint-php.yml b/.github/workflows/test-and-lint-php.yml index 430fdf6..d287df0 100644 --- a/.github/workflows/test-and-lint-php.yml +++ b/.github/workflows/test-and-lint-php.yml @@ -10,6 +10,16 @@ jobs: test-and-lint-php: name: Test & lint PHP stuff runs-on: ubuntu-20.04 + services: + mysql: + image: mysql:8.0 + env: + MYSQL_DATABASE: toby + MYSQL_USER: toby + MYSQL_PASSWORD: password + MYSQL_ALLOW_EMPTY_PASSWORD: 1 + ports: + - 3306:3306 steps: - uses: actions/checkout@v2 diff --git a/composer.json b/composer.json index 83abbee..d597c15 100644 --- a/composer.json +++ b/composer.json @@ -20,6 +20,7 @@ "enlightn/enlightn": "^1.22", "facade/ignition": "^2.5", "fakerphp/faker": "^1.9.1", + "laravel/dusk": "^6.21", "mockery/mockery": "^1.4.4", "nunomaduro/collision": "^5.10", "phpunit/phpunit": "^9.5.10" diff --git a/composer.lock b/composer.lock index e9f029f..f58b160 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "6c0c7586f9003a71d9299165b3d5030d", + "content-hash": "f5d5c19c36f856c3d336c36a43aa23e4", "packages": [ { "name": "asm89/stack-cors", @@ -6864,6 +6864,79 @@ }, "time": "2021-07-22T09:24:00+00:00" }, + { + "name": "laravel/dusk", + "version": "v6.21.0", + "source": { + "type": "git", + "url": "https://github.com/laravel/dusk.git", + "reference": "45c1005ec73ab46504a666b6b52dddf1108b3eb4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/dusk/zipball/45c1005ec73ab46504a666b6b52dddf1108b3eb4", + "reference": "45c1005ec73ab46504a666b6b52dddf1108b3eb4", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-zip": "*", + "illuminate/console": "^6.0|^7.0|^8.0|^9.0", + "illuminate/support": "^6.0|^7.0|^8.0|^9.0", + "nesbot/carbon": "^2.0", + "php": "^7.2|^8.0", + "php-webdriver/webdriver": "^1.9.0", + "symfony/console": "^4.3|^5.0|^6.0", + "symfony/finder": "^4.3|^5.0|^6.0", + "symfony/process": "^4.3|^5.0|^6.0", + "vlucas/phpdotenv": "^3.0|^4.0|^5.2" + }, + "require-dev": { + "mockery/mockery": "^1.0", + "orchestra/testbench": "^4.16|^5.17.1|^6.12.1|^7.0", + "phpunit/phpunit": "^7.5.15|^8.4|^9.0" + }, + "suggest": { + "ext-pcntl": "Used to gracefully terminate Dusk when tests are running." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "6.x-dev" + }, + "laravel": { + "providers": [ + "Laravel\\Dusk\\DuskServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Laravel\\Dusk\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "Laravel Dusk provides simple end-to-end testing and browser automation.", + "keywords": [ + "laravel", + "testing", + "webdriver" + ], + "support": { + "issues": "https://github.com/laravel/dusk/issues", + "source": "https://github.com/laravel/dusk/tree/v6.21.0" + }, + "time": "2022-01-12T18:00:01+00:00" + }, { "name": "mockery/mockery", "version": "1.4.4", @@ -7290,6 +7363,71 @@ }, "time": "2021-02-23T14:00:09+00:00" }, + { + "name": "php-webdriver/webdriver", + "version": "1.12.0", + "source": { + "type": "git", + "url": "https://github.com/php-webdriver/php-webdriver.git", + "reference": "99d4856ed7dffcdf6a52eccd6551e83d8d557ceb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-webdriver/php-webdriver/zipball/99d4856ed7dffcdf6a52eccd6551e83d8d557ceb", + "reference": "99d4856ed7dffcdf6a52eccd6551e83d8d557ceb", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "ext-json": "*", + "ext-zip": "*", + "php": "^5.6 || ~7.0 || ^8.0", + "symfony/polyfill-mbstring": "^1.12", + "symfony/process": "^2.8 || ^3.1 || ^4.0 || ^5.0 || ^6.0" + }, + "replace": { + "facebook/webdriver": "*" + }, + "require-dev": { + "ondram/ci-detector": "^2.1 || ^3.5 || ^4.0", + "php-coveralls/php-coveralls": "^2.4", + "php-mock/php-mock-phpunit": "^1.1 || ^2.0", + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpunit/phpunit": "^5.7 || ^7 || ^8 || ^9", + "squizlabs/php_codesniffer": "^3.5", + "symfony/var-dumper": "^3.3 || ^4.0 || ^5.0 || ^6.0" + }, + "suggest": { + "ext-SimpleXML": "For Firefox profile creation" + }, + "type": "library", + "autoload": { + "psr-4": { + "Facebook\\WebDriver\\": "lib/" + }, + "files": [ + "lib/Exception/TimeoutException.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A PHP client for Selenium WebDriver. Previously facebook/webdriver.", + "homepage": "https://github.com/php-webdriver/php-webdriver", + "keywords": [ + "Chromedriver", + "geckodriver", + "php", + "selenium", + "webdriver" + ], + "support": { + "issues": "https://github.com/php-webdriver/php-webdriver/issues", + "source": "https://github.com/php-webdriver/php-webdriver/tree/1.12.0" + }, + "time": "2021-10-14T09:30:02+00:00" + }, { "name": "phpdocumentor/reflection-common", "version": "2.2.0", diff --git a/config/database.php b/config/database.php index 6408789..c3831fc 100644 --- a/config/database.php +++ b/config/database.php @@ -5,11 +5,6 @@ declare(strict_types=1); return [ "default" => env("DB_CONNECTION", "mysql"), "connections" => [ - "testing" => [ - "driver" => "sqlite", - "database" => ":memory:", - "prefix" => "", - ], "mysql" => [ "driver" => "mysql", "url" => env("DATABASE_URL"), diff --git a/docker-compose.yml b/docker-compose.yml index 851001f..e649c7a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -36,12 +36,12 @@ services: database: image: mysql:8.0 - container_name: toby-db + container_name: toby-db-dev environment: - - MYSQL_ROOT_PASSWORD=${DOCKER_DB_ROOT_PASSWORD} - - MYSQL_DATABASE=${DOCKER_DB_DATABASE} - - MYSQL_USER=${DOCKER_DB_USERNAME} - - MYSQL_PASSWORD=${DOCKER_DB_PASSWORD} + - MYSQL_ROOT_PASSWORD=${DOCKER_DEV_DB_ROOT_PASSWORD} + - MYSQL_DATABASE=${DOCKER_DEV_DB_DATABASE} + - MYSQL_USER=${DOCKER_DEV_DB_USERNAME} + - MYSQL_PASSWORD=${DOCKER_DEV_DB_PASSWORD} ports: - ${DOCKER_DB_EXTERNAL_PORT:-3306}:3306 volumes: @@ -50,6 +50,20 @@ services: - toby-dev restart: unless-stopped + database-test: + image: mysql:8.0 + container_name: toby-db-test + environment: + - MYSQL_ROOT_PASSWORD=${DOCKER_TEST_DB_ROOT_PASSWORD} + - MYSQL_DATABASE=${DOCKER_TEST_DB_DATABASE} + - MYSQL_USER=${DOCKER_TEST_DB_USERNAME} + - MYSQL_PASSWORD=${DOCKER_TEST_DB_PASSWORD} + ports: + - ${DOCKER_TEST_DB_EXTERNAL_PORT:-3307}:3306 + networks: + - toby-dev + restart: unless-stopped + node: image: node:17.2.0-alpine3.14 container_name: toby-node @@ -70,6 +84,14 @@ services: - toby-dev restart: unless-stopped + selenium: + image: selenium/standalone-chrome + container_name: toby-selenium + volumes: + - /dev/shm:/dev/shm + networks: + - toby-dev + networks: toby-dev: driver: bridge diff --git a/package-lock.json b/package-lock.json index f5d5036..8cb4e0f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,5 +1,5 @@ { - "name": "toby", + "name": "application", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/phpunit.xml b/phpunit.xml index c4a1bad..c1b2a5c 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -19,14 +19,17 @@ - - - - - - - - - + + + + + + + + + + + + diff --git a/resources/js/Pages/Login.vue b/resources/js/Pages/Login.vue index 66b0f0f..e3ba099 100644 --- a/resources/js/Pages/Login.vue +++ b/resources/js/Pages/Login.vue @@ -38,6 +38,7 @@
Open user menu user = User::factory()->create(); + } + + public function testUserCanLogout(): void + { + $this->browse(function (Browser $browser): void { + $browser->loginAs($this->user) + ->visit(new HomePage()) + ->assertVisible("@user-menu") + ->click("@user-menu") + ->assertVisible("@user-menu-list") + ->assertSee("Sign out") + ->press("Sign out") + ->on(new HomePage()) + ->waitFor("@login-link"); + }); + } +} diff --git a/tests/Browser/Pages/HomePage.php b/tests/Browser/Pages/HomePage.php new file mode 100644 index 0000000..8e12114 --- /dev/null +++ b/tests/Browser/Pages/HomePage.php @@ -0,0 +1,20 @@ +assertPathIs($this->url()); + } +} diff --git a/tests/Browser/Pages/Page.php b/tests/Browser/Pages/Page.php new file mode 100644 index 0000000..954c185 --- /dev/null +++ b/tests/Browser/Pages/Page.php @@ -0,0 +1,17 @@ + "#selector", + ]; + } +} diff --git a/tests/DuskTestCase.php b/tests/DuskTestCase.php new file mode 100644 index 0000000..baaee44 --- /dev/null +++ b/tests/DuskTestCase.php @@ -0,0 +1,65 @@ +addArguments( + collect( + [ + "--window-size=1920,1080", + ], + )->unless( + $this->hasHeadlessDisabled(), + function ($items) { + return $items->merge( + [ + "--disable-gpu", + "--headless", + ], + ); + }, + )->all(), + ); + + return RemoteWebDriver::create( + env("DUSK_DRIVER_URL") ?? "http://localhost:" . env("SELENIUM_PORT"), + DesiredCapabilities::chrome()->setCapability( + ChromeOptions::CAPABILITY, + $options, + ), + ); + } + + protected function hasHeadlessDisabled(): bool + { + return isset($_SERVER["DUSK_HEADLESS_DISABLED"]) || + isset($_ENV["DUSK_HEADLESS_DISABLED"]); + } + + protected static function runningInDocker(): bool + { + return isset($_ENV["DUSK_IN_DOCKER"]) && $_ENV["DUSK_IN_DOCKER"] === "true"; + } +}