#17 - set up Laravel Dusk (#18)

* #17 - set up Laravel Dusk

* #17 - ecs fix

* #17 - fix

* #17 - fix

* #17 - fix

* #17 - fix

* Update .env.dusk.local

Co-authored-by: Krzysztof Rewak <krzysztof.rewak@blumilk.pl>

Co-authored-by: Adrian Hopek <adrian.hopek@blumilk.pl>
Co-authored-by: Krzysztof Rewak <krzysztof.rewak@blumilk.pl>
This commit is contained in:
Ewelina Lasowy 2022-01-18 12:22:02 +01:00 committed by GitHub
parent 0869395aab
commit 91bd46cc36
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 397 additions and 27 deletions

23
.env.ci Normal file
View File

@ -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

28
.env.dusk.local Normal file
View File

@ -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

View File

@ -8,18 +8,24 @@ LOG_CHANNEL=stack
LOG_LEVEL=debug LOG_LEVEL=debug
DB_CONNECTION=mysql DB_CONNECTION=mysql
DB_HOST=toby-db DB_HOST=toby-db-dev
DB_PORT=3306 DB_PORT=3306
DB_DATABASE=toby DB_DATABASE=toby
DB_USERNAME=toby DB_USERNAME=toby
DB_PASSWORD=password DB_PASSWORD=password
DB_ROOT_PASSWORD=example DB_ROOT_PASSWORD=example
DOCKER_DB_EXTERNAL_PORT=3306 DOCKER_DEV_DB_EXTERNAL_PORT=3306
DOCKER_DB_DATABASE=${DB_DATABASE} DOCKER_DEV_DB_DATABASE=${DB_DATABASE}
DOCKER_DB_USERNAME=${DB_USERNAME} DOCKER_DEV_DB_USERNAME=${DB_USERNAME}
DOCKER_DB_PASSWORD=${DB_PASSWORD} DOCKER_DEV_DB_PASSWORD=${DB_PASSWORD}
DOCKER_DB_ROOT_PASSWORD=${DB_ROOT_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= EXTERNAL_WEBSERVER_PORT=
XDG_CONFIG_HOME=/tmp XDG_CONFIG_HOME=/tmp

View File

@ -10,6 +10,16 @@ jobs:
test-and-lint-php: test-and-lint-php:
name: Test & lint PHP stuff name: Test & lint PHP stuff
runs-on: ubuntu-20.04 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: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2

View File

@ -20,6 +20,7 @@
"enlightn/enlightn": "^1.22", "enlightn/enlightn": "^1.22",
"facade/ignition": "^2.5", "facade/ignition": "^2.5",
"fakerphp/faker": "^1.9.1", "fakerphp/faker": "^1.9.1",
"laravel/dusk": "^6.21",
"mockery/mockery": "^1.4.4", "mockery/mockery": "^1.4.4",
"nunomaduro/collision": "^5.10", "nunomaduro/collision": "^5.10",
"phpunit/phpunit": "^9.5.10" "phpunit/phpunit": "^9.5.10"

140
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "6c0c7586f9003a71d9299165b3d5030d", "content-hash": "f5d5c19c36f856c3d336c36a43aa23e4",
"packages": [ "packages": [
{ {
"name": "asm89/stack-cors", "name": "asm89/stack-cors",
@ -6864,6 +6864,79 @@
}, },
"time": "2021-07-22T09:24:00+00:00" "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", "name": "mockery/mockery",
"version": "1.4.4", "version": "1.4.4",
@ -7290,6 +7363,71 @@
}, },
"time": "2021-02-23T14:00:09+00:00" "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", "name": "phpdocumentor/reflection-common",
"version": "2.2.0", "version": "2.2.0",

View File

@ -5,11 +5,6 @@ declare(strict_types=1);
return [ return [
"default" => env("DB_CONNECTION", "mysql"), "default" => env("DB_CONNECTION", "mysql"),
"connections" => [ "connections" => [
"testing" => [
"driver" => "sqlite",
"database" => ":memory:",
"prefix" => "",
],
"mysql" => [ "mysql" => [
"driver" => "mysql", "driver" => "mysql",
"url" => env("DATABASE_URL"), "url" => env("DATABASE_URL"),

View File

@ -36,12 +36,12 @@ services:
database: database:
image: mysql:8.0 image: mysql:8.0
container_name: toby-db container_name: toby-db-dev
environment: environment:
- MYSQL_ROOT_PASSWORD=${DOCKER_DB_ROOT_PASSWORD} - MYSQL_ROOT_PASSWORD=${DOCKER_DEV_DB_ROOT_PASSWORD}
- MYSQL_DATABASE=${DOCKER_DB_DATABASE} - MYSQL_DATABASE=${DOCKER_DEV_DB_DATABASE}
- MYSQL_USER=${DOCKER_DB_USERNAME} - MYSQL_USER=${DOCKER_DEV_DB_USERNAME}
- MYSQL_PASSWORD=${DOCKER_DB_PASSWORD} - MYSQL_PASSWORD=${DOCKER_DEV_DB_PASSWORD}
ports: ports:
- ${DOCKER_DB_EXTERNAL_PORT:-3306}:3306 - ${DOCKER_DB_EXTERNAL_PORT:-3306}:3306
volumes: volumes:
@ -50,6 +50,20 @@ services:
- toby-dev - toby-dev
restart: unless-stopped 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: node:
image: node:17.2.0-alpine3.14 image: node:17.2.0-alpine3.14
container_name: toby-node container_name: toby-node
@ -70,6 +84,14 @@ services:
- toby-dev - toby-dev
restart: unless-stopped restart: unless-stopped
selenium:
image: selenium/standalone-chrome
container_name: toby-selenium
volumes:
- /dev/shm:/dev/shm
networks:
- toby-dev
networks: networks:
toby-dev: toby-dev:
driver: bridge driver: bridge

2
package-lock.json generated
View File

@ -1,5 +1,5 @@
{ {
"name": "toby", "name": "application",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {

View File

@ -19,14 +19,17 @@
</coverage> </coverage>
<php> <php>
<server name="APP_ENV" value="testing"/> <server name="APP_ENV" value="testing"/>
<server name="APP_KEY" value="base64:SKEJSy9oF9chQBCMbxqgj5zhtAvug9kwZ+cDiP1Y8A8="/> <env name="APP_KEY" value="base64:SKEJSy9oF9chQBCMbxqgj5zhtAvug9kwZ+cDiP1Y8A8="/>
<server name="BCRYPT_ROUNDS" value="4"/> <env name="BCRYPT_ROUNDS" value="4"/>
<server name="CACHE_DRIVER" value="array"/> <env name="CACHE_DRIVER" value="array"/>
<server name="DB_CONNECTION" value="testing"/> <env name="DB_CONNECTION" value="mysql"/>
<server name="DB_DATABASE" value=":memory:"/> <env name="DB_HOST" value="toby-db-test"/>
<server name="MAIL_MAILER" value="array"/> <env name="DB_DATABASE" value="toby"/>
<server name="QUEUE_CONNECTION" value="sync"/> <env name="DB_USERNAME" value="toby"/>
<server name="SESSION_DRIVER" value="array"/> <env name="DB_PASSWORD" value="password"/>
<server name="TELESCOPE_ENABLED" value="false"/> <env name="MAIL_MAILER" value="array"/>
<env name="QUEUE_CONNECTION" value="sync"/>
<env name="SESSION_DRIVER" value="array"/>
<env name="TELESCOPE_ENABLED" value="false"/>
</php> </php>
</phpunit> </phpunit>

View File

@ -38,6 +38,7 @@
</transition> </transition>
<div <div
class="sm:mx-auto sm:w-full sm:max-w-md text-white space-y-4 flex flex-col items-center rounded-lg px-4 py-8" class="sm:mx-auto sm:w-full sm:max-w-md text-white space-y-4 flex flex-col items-center rounded-lg px-4 py-8"
dusk="login-link"
> >
<img <img
class="mx-auto h-50 w-auto" class="mx-auto h-50 w-auto"

View File

@ -38,6 +38,7 @@
<div> <div>
<MenuButton <MenuButton
class="bg-white rounded-full flex text-sm ring-2 ring-white ring-opacity-20 focus:outline-none focus:ring-opacity-100" class="bg-white rounded-full flex text-sm ring-2 ring-white ring-opacity-20 focus:outline-none focus:ring-opacity-100"
dusk="user-menu"
> >
<span class="sr-only">Open user menu</span> <span class="sr-only">Open user menu</span>
<img <img
@ -54,6 +55,7 @@
> >
<MenuItems <MenuItems
class="origin-top-right z-40 absolute -right-2 mt-2 w-48 rounded-md shadow-lg py-1 bg-white ring-1 ring-black ring-opacity-5 focus:outline-none" class="origin-top-right z-40 absolute -right-2 mt-2 w-48 rounded-md shadow-lg py-1 bg-white ring-1 ring-black ring-opacity-5 focus:outline-none"
dusk="user-menu-list"
> >
<MenuItem <MenuItem
v-for="item in userNavigation" v-for="item in userNavigation"

View File

@ -0,0 +1,39 @@
<?php
declare(strict_types=1);
namespace Tests\Browser;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Laravel\Dusk\Browser;
use Tests\Browser\Pages\HomePage;
use Tests\DuskTestCase;
use Toby\Models\User;
class AuthenticationTest extends DuskTestCase
{
use DatabaseMigrations;
protected User $user;
protected function setUp(): void
{
parent::setUp();
$this->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");
});
}
}

View File

@ -0,0 +1,20 @@
<?php
declare(strict_types=1);
namespace Tests\Browser\Pages;
use Laravel\Dusk\Browser;
class HomePage extends Page
{
public function url()
{
return "/";
}
public function assert(Browser $browser): void
{
$browser->assertPathIs($this->url());
}
}

View File

@ -0,0 +1,17 @@
<?php
declare(strict_types=1);
namespace Tests\Browser\Pages;
use Laravel\Dusk\Page as BasePage;
abstract class Page extends BasePage
{
public static function siteElements()
{
return [
"@element" => "#selector",
];
}
}

65
tests/DuskTestCase.php Normal file
View File

@ -0,0 +1,65 @@
<?php
declare(strict_types=1);
namespace Tests;
use Facebook\WebDriver\Chrome\ChromeOptions;
use Facebook\WebDriver\Remote\DesiredCapabilities;
use Facebook\WebDriver\Remote\RemoteWebDriver;
use Laravel\Dusk\TestCase as BaseTestCase;
abstract class DuskTestCase extends BaseTestCase
{
use CreatesApplication;
/**
* @beforeClass
*/
public static function prepare(): void
{
if (!static::runningInDocker()) {
static::startChromeDriver();
}
}
protected function driver(): RemoteWebDriver
{
$options = (new ChromeOptions())->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";
}
}