Laravel Dusk
- Introduction
- Installation
- Getting Started
- Browser Basics
- Interacting With Elements
- Available Assertions
- Pages
- Components
- Continuous Integration
Introduction
Laravel Dusk는 표현력 있고 사용하기 쉬운 브라우저 자동화 및 테스트 API를 제공합니다. 기본적으로 Dusk는 로컬 컴퓨터에 JDK나 Selenium을 설치하지 않아도 됩니다. 대신 Dusk는 독립 실행형 ChromeDriver 설치를 사용합니다. 다만 원하는 다른 Selenium 호환 드라이버를 자유롭게 사용할 수 있습니다.
Installation
시작하려면 Google Chrome을 설치하고 프로젝트에 laravel/dusk Composer 의존성을 추가해야 합니다.
composer require --dev laravel/dusk
Dusk의 서비스 프로바이더를 수동으로 등록한다면 프로덕션 환경에는 절대 등록하지 않아야 합니다. 그렇게 하면 임의의 사용자가 애플리케이션에 인증할 수 있게 될 수 있습니다.
Dusk 패키지를 설치한 뒤 dusk:install Artisan 명령어를 실행합니다. dusk:install 명령어는 tests/Browser 디렉터리와 예제 Dusk 테스트를 생성합니다.
php artisan dusk:install
다음으로 애플리케이션의 .env 파일에서 APP_URL 환경 변수를 설정합니다. 이 값은 브라우저에서 애플리케이션에 접속할 때 사용하는 URL과 일치해야 합니다.
로컬 개발 환경을 관리하는 데 Laravel Sail을 사용한다면 configuring and running Dusk tests에 관한 Sail 문서도 참고하십시오.
Managing ChromeDriver Installations
Laravel Dusk에 포함된 버전과 다른 ChromeDriver 버전을 설치하려면 dusk:chrome-driver 명령어를 사용할 수 있습니다.
# Install the latest version of ChromeDriver for your OS...
php artisan dusk:chrome-driver
# Install a given version of ChromeDriver for your OS...
php artisan dusk:chrome-driver 86
# Install a given version of ChromeDriver for all supported OSs...
php artisan dusk:chrome-driver --all
# Install the version of ChromeDriver that matches the detected version of Chrome / Chromium for your OS...
php artisan dusk:chrome-driver --detect
Dusk는
chromedriver바이너리가 실행 가능해야 합니다. Dusk 실행에 문제가 있다면 다음 명령어를 사용해 바이너리를 실행 가능하게 설정했는지 확인해야 합니다.chmod -R 0755 vendor/laravel/dusk/bin/.
Using Other Browsers
기본적으로 Dusk는 Google Chrome과 독립 실행형 ChromeDriver 설치를 사용해 브라우저 테스트를 실행합니다. 하지만 직접 Selenium 서버를 시작하고 원하는 브라우저를 대상으로 테스트를 실행할 수도 있습니다.
시작하려면 애플리케이션의 기본 Dusk 테스트 케이스인 tests/DuskTestCase.php 파일을 엽니다. 이 파일에서 startChromeDriver 메서드 호출을 제거할 수 있습니다. 이렇게 하면 Dusk가 ChromeDriver를 자동으로 시작하지 않습니다.
/**
* Prepare for Dusk test execution.
*
* @beforeClass
* @return void
*/
public static function prepare()
{
// static::startChromeDriver();
}
다음으로 원하는 URL과 포트에 연결하도록 driver 메서드를 수정할 수 있습니다. 또한 WebDriver에 전달할 "desired capabilities"도 수정할 수 있습니다.
/**
* Create the RemoteWebDriver instance.
*
* @return \Facebook\WebDriver\Remote\RemoteWebDriver
*/
protected function driver()
{
return RemoteWebDriver::create(
'http://localhost:4444/wd/hub', DesiredCapabilities::phantomjs()
);
}
Getting Started
Generating Tests
Dusk 테스트를 생성하려면 dusk:make Artisan 명령어를 사용합니다. 생성된 테스트는 tests/Browser 디렉터리에 배치됩니다.
php artisan dusk:make LoginTest
Database Migrations
작성하는 대부분의 테스트는 애플리케이션 데이터베이스에서 데이터를 가져오는 페이지와 상호작용합니다. 하지만 Dusk 테스트에서는 RefreshDatabase 트레이트를 절대 사용하지 않아야 합니다. RefreshDatabase 트레이트는 데이터베이스 트랜잭션을 활용하는데, HTTP 요청 간에는 적용하거나 사용할 수 없습니다. 대신 각 테스트마다 데이터베이스를 다시 마이그레이션하는 DatabaseMigrations 트레이트를 사용하십시오.
<?php
namespace Tests\Browser;
use App\Models\User;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Laravel\Dusk\Chrome;
use Tests\DuskTestCase;
class ExampleTest extends DuskTestCase
{
use DatabaseMigrations;
}
Dusk 테스트를 실행할 때는 SQLite 인메모리 데이터베이스를 사용할 수 없습니다. 브라우저는 자체 프로세스 안에서 실행되므로 다른 프로세스의 인메모리 데이터베이스에 접근할 수 없습니다.
Running Tests
브라우저 테스트를 실행하려면 dusk Artisan 명령어를 실행합니다.
php artisan dusk
마지막으로 dusk 명령어를 실행했을 때 실패한 테스트가 있었다면, dusk:fails 명령어를 사용해 실패한 테스트를 먼저 다시 실행하여 시간을 아낄 수 있습니다.
php artisan dusk:fails
dusk 명령어는 PHPUnit 테스트 러너가 일반적으로 허용하는 모든 인수를 받을 수 있습니다. 예를 들어 특정 group의 테스트만 실행할 수 있습니다.
php artisan dusk --group=foo
로컬 개발 환경을 관리하는 데 Laravel Sail을 사용한다면 configuring and running Dusk tests에 관한 Sail 문서를 참고하십시오.
Manually Starting ChromeDriver
기본적으로 Dusk는 ChromeDriver를 자동으로 시작하려고 시도합니다. 특정 시스템에서 이 동작이 작동하지 않는다면 dusk 명령어를 실행하기 전에 ChromeDriver를 수동으로 시작할 수 있습니다. ChromeDriver를 수동으로 시작하기로 했다면 tests/DuskTestCase.php 파일의 다음 줄을 주석 처리해야 합니다.
/**
* Prepare for Dusk test execution.
*
* @beforeClass
* @return void
*/
public static function prepare()
{
// static::startChromeDriver();
}
또한 ChromeDriver를 9515가 아닌 다른 포트에서 시작한다면 올바른 포트를 반영하도록 같은 클래스의 driver 메서드를 수정해야 합니다.
/**
* Create the RemoteWebDriver instance.
*
* @return \Facebook\WebDriver\Remote\RemoteWebDriver
*/
protected function driver()
{
return RemoteWebDriver::create(
'http://localhost:9515', DesiredCapabilities::chrome()
);
}
Environment Handling
테스트를 실행할 때 Dusk가 자체 환경 파일을 사용하도록 강제하려면 프로젝트 루트에 .env.dusk.{environment} 파일을 생성합니다. 예를 들어 local 환경에서 dusk 명령어를 시작한다면 .env.dusk.local 파일을 생성해야 합니다.
테스트를 실행할 때 Dusk는 .env 파일을 백업하고 Dusk 환경 파일의 이름을 .env로 변경합니다. 테스트가 완료되면 .env 파일이 복원됩니다.
Browser Basics
Creating Browsers
시작하기 위해 애플리케이션에 로그인할 수 있는지 확인하는 테스트를 작성해 보겠습니다. 테스트를 생성한 뒤 로그인 페이지로 이동하고, 자격 증명을 입력한 다음, "Login" 버튼을 클릭하도록 수정할 수 있습니다. 브라우저 인스턴스를 생성하려면 Dusk 테스트 안에서 browse 메서드를 호출하면 됩니다.
<?php
namespace Tests\Browser;
use App\Models\User;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Laravel\Dusk\Chrome;
use Tests\DuskTestCase;
class ExampleTest extends DuskTestCase
{
use DatabaseMigrations;
/**
* A basic browser test example.
*
* @return void
*/
public function test_basic_example()
{
$user = User::factory()->create([
]);
$this->browse(function ($browser) use ($user) {
$browser->visit('/login')
->type('email', $user->email)
->type('password', 'password')
->press('Login')
->assertPathIs('/home');
});
}
}
위 예시에서 볼 수 있듯이 browse 메서드는 클로저를 받습니다. Dusk는 이 클로저에 브라우저 인스턴스를 자동으로 전달하며, 이 인스턴스는 애플리케이션과 상호작용하고 assertion을 수행하는 데 사용하는 핵심 객체입니다.
Creating Multiple Browsers
테스트를 제대로 수행하기 위해 여러 브라우저가 필요할 때도 있습니다. 예를 들어 websockets와 상호작용하는 채팅 화면을 테스트하려면 여러 브라우저가 필요할 수 있습니다. 여러 브라우저를 생성하려면 browse 메서드에 전달하는 클로저의 시그니처에 브라우저 인수를 더 추가하면 됩니다.
$this->browse(function ($first, $second) {
$first->loginAs(User::find(1))
->visit('/home')
->waitForText('Message');
$second->loginAs(User::find(2))
->visit('/home')
->waitForText('Message')
->type('message', 'Hey Taylor')
->press('Send');
$first->waitForText('Hey Taylor')
->assertSee('Jeffrey Way');
});
Navigation
visit 메서드는 애플리케이션 안의 지정한 URI로 이동하는 데 사용할 수 있습니다.
$browser->visit('/login');
visitRoute 메서드를 사용해 named route로 이동할 수 있습니다.
$browser->visitRoute('login');
back과 forward 메서드를 사용해 "뒤로" 및 "앞으로" 이동할 수 있습니다.
$browser->back();
$browser->forward();
refresh 메서드를 사용해 페이지를 새로고침할 수 있습니다.
$browser->refresh();
Resizing Browser Windows
resize 메서드를 사용해 브라우저 창 크기를 조정할 수 있습니다.
$browser->resize(1920, 1080);
maximize 메서드는 브라우저 창을 최대화하는 데 사용할 수 있습니다.
$browser->maximize();
fitContent 메서드는 브라우저 창 크기를 콘텐츠 크기에 맞게 조정합니다.
$browser->fitContent();
테스트가 실패하면 Dusk는 스크린샷을 찍기 전에 브라우저 크기를 콘텐츠에 맞게 자동으로 조정합니다. 테스트 안에서 disableFitOnFailure 메서드를 호출해 이 기능을 비활성화할 수 있습니다.
$browser->disableFitOnFailure();
move 메서드를 사용해 브라우저 창을 화면의 다른 위치로 옮길 수 있습니다.
$browser->move($x = 100, $y = 100);
Browser Macros
여러 테스트에서 재사용할 수 있는 커스텀 브라우저 메서드를 정의하려면 Browser 클래스의 macro 메서드를 사용할 수 있습니다. 일반적으로 이 메서드는 service provider's의 boot 메서드에서 호출해야 합니다.
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Laravel\Dusk\Browser;
class DuskServiceProvider extends ServiceProvider
{
/**
* Register Dusk's browser macros.
*
* @return void
*/
public function boot()
{
Browser::macro('scrollToElement', function ($element = null) {
$this->script("$('html, body').animate({ scrollTop: $('$element').offset().top }, 0);");
return $this;
});
}
}
macro 함수는 첫 번째 인수로 이름을, 두 번째 인수로 클로저를 받습니다. Browser 인스턴스에서 매크로를 메서드처럼 호출하면 매크로의 클로저가 실행됩니다.
$this->browse(function ($browser) use ($user) {
$browser->visit('/pay')
->scrollToElement('#credit-card-details')
->assertSee('Enter Credit Card Details');
});
Authentication
인증이 필요한 페이지를 테스트하는 경우가 많습니다. 매 테스트마다 애플리케이션의 로그인 화면과 상호작용하지 않으려면 Dusk의 loginAs 메서드를 사용할 수 있습니다. loginAs 메서드는 인증 가능한 모델과 연결된 기본 키 또는 인증 가능한 모델 인스턴스를 인수로 받습니다.
use App\Models\User;
$this->browse(function ($browser) {
$browser->loginAs(User::find(1))
->visit('/home');
});
loginAs메서드를 사용한 뒤에는 파일 안의 모든 테스트에서 사용자 세션이 유지됩니다.
Cookies
cookie 메서드를 사용해 암호화된 쿠키의 값을 가져오거나 설정할 수 있습니다. 기본적으로 Laravel이 생성하는 모든 쿠키는 암호화됩니다.
$browser->cookie('name');
$browser->cookie('name', 'Taylor');
plainCookie 메서드를 사용해 암호화되지 않은 쿠키의 값을 가져오거나 설정할 수 있습니다.
$browser->plainCookie('name');
$browser->plainCookie('name', 'Taylor');
deleteCookie 메서드를 사용해 지정한 쿠키를 삭제할 수 있습니다.
$browser->deleteCookie('name');
Executing JavaScript
script 메서드를 사용해 브라우저 안에서 임의의 JavaScript 문을 실행할 수 있습니다.
$browser->script('document.documentElement.scrollTop = 0');
$browser->script([
'document.body.scrollTop = 0',
'document.documentElement.scrollTop = 0',
]);
$output = $browser->script('return window.location.pathname');
Taking A Screenshot
screenshot 메서드를 사용해 스크린샷을 찍고 지정한 파일명으로 저장할 수 있습니다. 모든 스크린샷은 tests/Browser/screenshots 디렉터리에 저장됩니다.
$browser->screenshot('filename');
Storing Console Output To Disk
storeConsoleLog 메서드를 사용해 현재 브라우저의 콘솔 출력을 지정한 파일명으로 디스크에 쓸 수 있습니다. 콘솔 출력은 tests/Browser/console 디렉터리에 저장됩니다.
$browser->storeConsoleLog('filename');
Storing Page Source To Disk
storeSource 메서드를 사용해 현재 페이지의 소스를 지정한 파일명으로 디스크에 쓸 수 있습니다. 페이지 소스는 tests/Browser/source 디렉터리에 저장됩니다.
$browser->storeSource('filename');
Interacting With Elements
Dusk Selectors
요소와 상호작용할 좋은 CSS 셀렉터를 고르는 일은 Dusk 테스트 작성에서 가장 어려운 부분 중 하나입니다. 시간이 지나며 프런트엔드가 변경되면 다음과 같은 CSS 셀렉터 때문에 테스트가 깨질 수 있습니다.
// HTML...
<button>Login</button>
// Test...
$browser->click('.login-page .container div > button');
Dusk 셀렉터를 사용하면 CSS 셀렉터를 기억하는 대신 효과적인 테스트 작성에 집중할 수 있습니다. 셀렉터를 정의하려면 HTML 요소에 dusk 속성을 추가합니다. 그런 다음 Dusk 브라우저와 상호작용할 때 셀렉터 앞에 @를 붙여 테스트 안에서 연결된 요소를 조작합니다.
// HTML...
<button dusk="login-button">Login</button>
// Test...
$browser->click('@login-button');
Text, Values, & Attributes
Retrieving & Setting Values
Dusk는 페이지 요소의 현재 값, 표시 텍스트, 속성과 상호작용하는 여러 메서드를 제공합니다. 예를 들어 주어진 CSS 또는 Dusk 셀렉터와 일치하는 요소의 "value"를 가져오려면 value 메서드를 사용합니다.
// Retrieve the value...
$value = $browser->value('selector');
// Set the value...
$browser->value('selector', 'value');
inputValue 메서드를 사용해 지정한 필드 이름을 가진 input 요소의 "value"를 가져올 수 있습니다.
$value = $browser->inputValue('field');
Retrieving Text
text 메서드는 지정한 셀렉터와 일치하는 요소의 표시 텍스트를 가져올 때 사용할 수 있습니다.
$text = $browser->text('selector');
Retrieving Attributes
마지막으로 attribute 메서드를 사용해 지정한 셀렉터와 일치하는 요소의 속성 값을 가져올 수 있습니다.
$attribute = $browser->attribute('selector', 'value');
Interacting With Forms
Typing Values
Dusk는 폼과 input 요소와 상호작용하는 다양한 메서드를 제공합니다. 먼저 input 필드에 텍스트를 입력하는 예시를 살펴보겠습니다.
이 메서드는 필요한 경우 CSS 셀렉터를 받을 수 있지만, type 메서드에 CSS 셀렉터를 반드시 전달해야 하는 것은 아닙니다. CSS 셀렉터를 제공하지 않으면 Dusk는 지정한 name 속성을 가진 input 또는 textarea 필드를 찾습니다.
필드의 내용을 지우지 않고 텍스트를 덧붙이려면 append 메서드를 사용할 수 있습니다.
$browser->type('tags', 'foo')
->append('tags', ', bar, baz');
clear 메서드를 사용해 input의 값을 지울 수 있습니다.
$browser->clear('email');
typeSlowly 메서드를 사용하면 Dusk가 천천히 입력하도록 지시할 수 있습니다. 기본적으로 Dusk는 키를 누를 때마다 100밀리초 동안 일시 중지합니다. 키 입력 사이의 시간을 조정하려면 적절한 밀리초 값을 메서드의 세 번째 인수로 전달하면 됩니다.
$browser->typeSlowly('mobile', '+1 (202) 555-5555');
$browser->typeSlowly('mobile', '+1 (202) 555-5555', 300);
appendSlowly 메서드를 사용해 텍스트를 천천히 덧붙일 수 있습니다.
$browser->type('tags', 'foo')
->appendSlowly('tags', ', bar, baz');
Dropdowns
select 요소에서 사용할 수 있는 값을 선택하려면 select 메서드를 사용할 수 있습니다. type 메서드와 마찬가지로 select 메서드에도 전체 CSS 셀렉터가 필요하지 않습니다. select 메서드에 값을 전달할 때는 표시 텍스트가 아니라 실제 option 값을 전달해야 합니다.
$browser->select('size', 'Large');
두 번째 인수를 생략하면 임의의 옵션을 선택할 수 있습니다.
$browser->select('size');
select 메서드의 두 번째 인수로 배열을 제공하면 여러 옵션을 선택하도록 지시할 수 있습니다.
$browser->select('categories', ['Art', 'Music']);
Checkboxes
checkbox input을 "체크"하려면 check 메서드를 사용할 수 있습니다. 다른 여러 input 관련 메서드와 마찬가지로 전체 CSS 셀렉터는 필요하지 않습니다. 일치하는 CSS 셀렉터를 찾을 수 없으면 Dusk는 일치하는 name 속성을 가진 checkbox를 찾습니다.
$browser->check('terms');
uncheck 메서드는 checkbox input을 "체크 해제"할 때 사용할 수 있습니다.
$browser->uncheck('terms');
Radio Buttons
radio input 옵션을 "선택"하려면 radio 메서드를 사용할 수 있습니다. 다른 여러 input 관련 메서드와 마찬가지로 전체 CSS 셀렉터는 필요하지 않습니다. 일치하는 CSS 셀렉터를 찾을 수 없으면 Dusk는 일치하는 name 및 value 속성을 가진 radio input을 찾습니다.
$browser->radio('size', 'large');
Attaching Files
attach 메서드는 file input 요소에 파일을 첨부할 때 사용할 수 있습니다. 다른 여러 input 관련 메서드와 마찬가지로 전체 CSS 셀렉터는 필요하지 않습니다. 일치하는 CSS 셀렉터를 찾을 수 없으면 Dusk는 일치하는 name 속성을 가진 file input을 찾습니다.
$browser->attach('photo', __DIR__.'/photos/mountains.png');
attach 함수는 서버에
ZipPHP 확장이 설치되고 활성화되어 있어야 합니다.
Pressing Buttons
press 메서드는 페이지의 버튼 요소를 클릭할 때 사용할 수 있습니다. press 메서드에 전달하는 첫 번째 인수는 버튼의 표시 텍스트 또는 CSS / Dusk 셀렉터일 수 있습니다.
$browser->press('Login');
폼을 제출할 때 많은 애플리케이션은 폼의 제출 버튼을 누른 뒤 비활성화하고, 폼 제출의 HTTP 요청이 완료되면 다시 활성화합니다. 버튼을 누르고 버튼이 다시 활성화될 때까지 기다리려면 pressAndWaitFor 메서드를 사용할 수 있습니다.
// Press the button and wait a maximum of 5 seconds for it to be enabled...
$browser->pressAndWaitFor('Save');
// Press the button and wait a maximum of 1 second for it to be enabled...
$browser->pressAndWaitFor('Save', 1);
Clicking Links
링크를 클릭하려면 브라우저 인스턴스에서 clickLink 메서드를 사용할 수 있습니다. clickLink 메서드는 지정한 표시 텍스트가 있는 링크를 클릭합니다.
$browser->clickLink($linkText);
seeLink 메서드를 사용해 지정한 표시 텍스트가 있는 링크가 페이지에 보이는지 확인할 수 있습니다.
if ($browser->seeLink($linkText)) {
// ...
}
이 메서드들은 jQuery와 상호작용합니다. 페이지에서 jQuery를 사용할 수 없으면 Dusk가 테스트가 실행되는 동안 사용할 수 있도록 페이지에 자동으로 주입합니다.
Using The Keyboard
keys 메서드를 사용하면 type 메서드에서 일반적으로 허용하는 것보다 더 복잡한 입력 시퀀스를 지정한 요소에 제공할 수 있습니다. 예를 들어 값을 입력하는 동안 modifier 키를 누르고 있도록 Dusk에 지시할 수 있습니다. 이 예시에서는 지정한 셀렉터와 일치하는 요소에 taylor를 입력하는 동안 shift 키를 누른 상태로 유지합니다. taylor 입력이 끝나면 modifier 키 없이 swift가 입력됩니다.
$browser->keys('selector', ['{shift}', 'taylor'], 'swift');
keys 메서드의 또 다른 유용한 사용 사례는 애플리케이션의 기본 CSS 셀렉터에 "키보드 단축키" 조합을 보내는 것입니다.
$browser->keys('.app', ['{command}', 'j']);
{command}같은 모든 modifier 키는{}문자로 감싸며, found on GitHubFacebook\WebDriver\WebDriverKeys클래스에 정의된 상수와 일치합니다.
Using The Mouse
Clicking On Elements
click 메서드는 지정한 CSS 또는 Dusk 셀렉터와 일치하는 요소를 클릭할 때 사용할 수 있습니다.
$browser->click('.selector');
clickAtXPath 메서드는 지정한 XPath 표현식과 일치하는 요소를 클릭할 때 사용할 수 있습니다.
$browser->clickAtXPath('//div[@class = "selector"]');
clickAtPoint 메서드는 브라우저의 표시 영역을 기준으로 지정한 좌표 쌍에 있는 가장 위쪽 요소를 클릭할 때 사용할 수 있습니다.
$browser->clickAtPoint($x = 0, $y = 0);
doubleClick 메서드는 마우스 더블 클릭을 시뮬레이션할 때 사용할 수 있습니다.
$browser->doubleClick();
rightClick 메서드는 마우스 오른쪽 클릭을 시뮬레이션할 때 사용할 수 있습니다.
$browser->rightClick();
$browser->rightClick('.selector');
clickAndHold 메서드는 마우스 버튼을 클릭한 채 누르고 있는 동작을 시뮬레이션할 때 사용할 수 있습니다. 이어서 releaseMouse 메서드를 호출하면 이 동작을 되돌리고 마우스 버튼을 놓습니다.
$browser->clickAndHold()
->pause(1000)
->releaseMouse();
Mouseover
지정한 CSS 또는 Dusk 셀렉터와 일치하는 요소 위로 마우스를 이동해야 할 때 mouseover 메서드를 사용할 수 있습니다.
$browser->mouseover('.selector');
Drag & Drop
drag 메서드는 지정한 셀렉터와 일치하는 요소를 다른 요소로 드래그할 때 사용할 수 있습니다.
$browser->drag('.from-selector', '.to-selector');
또는 요소를 한 방향으로 드래그할 수 있습니다.
$browser->dragLeft('.selector', $pixels = 10);
$browser->dragRight('.selector', $pixels = 10);
$browser->dragUp('.selector', $pixels = 10);
$browser->dragDown('.selector', $pixels = 10);
마지막으로 지정한 오프셋만큼 요소를 드래그할 수 있습니다.
$browser->dragOffset('.selector', $x = 10, $y = 10);
JavaScript Dialogs
Dusk는 JavaScript 다이얼로그와 상호작용하는 다양한 메서드를 제공합니다. 예를 들어 waitForDialog 메서드를 사용해 JavaScript 다이얼로그가 나타날 때까지 기다릴 수 있습니다. 이 메서드는 다이얼로그가 나타날 때까지 몇 초 동안 기다릴지 나타내는 선택적 인수를 받습니다.
$browser->waitForDialog($seconds = null);
assertDialogOpened 메서드는 다이얼로그가 표시되었고 지정한 메시지를 포함하는지 단언할 때 사용할 수 있습니다.
$browser->assertDialogOpened('Dialog message');
JavaScript 대화 상자에 프롬프트가 포함되어 있다면, typeInDialog 메서드로 프롬프트에 값을 입력할 수 있습니다.
$browser->typeInDialog('Hello World');
열려 있는 JavaScript 대화 상자를 "OK" 버튼을 클릭해 닫으려면 acceptDialog 메서드를 호출하면 됩니다.
$browser->acceptDialog();
열려 있는 JavaScript 대화 상자를 "Cancel" 버튼을 클릭해 닫으려면 dismissDialog 메서드를 호출하면 됩니다.
$browser->dismissDialog();
Scoping Selectors
때로는 여러 작업을 모두 주어진 셀렉터 안으로 스코프를 제한한 상태에서 수행하고 싶을 수 있습니다. 예를 들어 어떤 텍스트가 테이블 안에만 존재한다고 어서션한 다음, 그 테이블 안의 버튼을 클릭하고 싶을 수 있습니다. 이때 with 메서드를 사용할 수 있습니다. with 메서드에 전달된 클로저 안에서 수행되는 모든 작업은 원래 셀렉터로 스코프가 제한됩니다.
$browser->with('.table', function ($table) {
$table->assertSee('Hello World')
->clickLink('Delete');
});
때로는 현재 스코프 밖에서 어서션을 실행해야 할 수도 있습니다. 이때 elsewhere와 elsewhereWhenAvailable 메서드를 사용할 수 있습니다.
$browser->with('.table', function ($table) {
// Current scope is `body .table`...
$browser->elsewhere('.page-title', function ($title) {
// Current scope is `body .page-title`...
$title->assertSee('Hello World');
});
$browser->elsewhereWhenAvailable('.page-title', function ($title) {
// Current scope is `body .page-title`...
$title->assertSee('Hello World');
});
});
Waiting For Elements
JavaScript를 많이 사용하는 애플리케이션을 테스트할 때는 테스트를 계속 진행하기 전에 특정 요소나 데이터를 사용할 수 있을 때까지 "기다려야" 하는 경우가 많습니다. Dusk는 이를 매우 쉽게 처리합니다. 다양한 메서드를 사용해 요소가 페이지에 표시될 때까지 기다리거나, 주어진 JavaScript 표현식이 true로 평가될 때까지 기다릴 수도 있습니다.
Waiting
테스트를 지정한 밀리초 동안 잠시 멈추기만 하면 된다면 pause 메서드를 사용합니다.
$browser->pause(1000);
Waiting For Selectors
waitFor 메서드는 주어진 CSS 또는 Dusk 셀렉터와 일치하는 요소가 페이지에 표시될 때까지 테스트 실행을 멈추는 데 사용할 수 있습니다. 기본적으로 예외를 던지기 전까지 최대 5초 동안 테스트를 멈춥니다. 필요하다면 메서드의 두 번째 인수로 사용자 지정 타임아웃 기준값을 전달할 수 있습니다.
// Wait a maximum of five seconds for the selector...
$browser->waitFor('.selector');
// Wait a maximum of one second for the selector...
$browser->waitFor('.selector', 1);
주어진 셀렉터와 일치하는 요소에 지정한 텍스트가 포함될 때까지 기다릴 수도 있습니다.
// Wait a maximum of five seconds for the selector to contain the given text...
$browser->waitForTextIn('.selector', 'Hello World');
// Wait a maximum of one second for the selector to contain the given text...
$browser->waitForTextIn('.selector', 'Hello World', 1);
주어진 셀렉터와 일치하는 요소가 페이지에서 사라질 때까지 기다릴 수도 있습니다.
// Wait a maximum of five seconds until the selector is missing...
$browser->waitUntilMissing('.selector');
// Wait a maximum of one second until the selector is missing...
$browser->waitUntilMissing('.selector', 1);
또는 주어진 셀렉터와 일치하는 요소가 활성화되거나 비활성화될 때까지 기다릴 수 있습니다.
// Wait a maximum of five seconds until the selector is enabled...
$browser->waitUntilEnabled('.selector');
// Wait a maximum of one second until the selector is enabled...
$browser->waitUntilEnabled('.selector', 1);
// Wait a maximum of five seconds until the selector is disabled...
$browser->waitUntilDisabled('.selector');
// Wait a maximum of one second until the selector is disabled...
$browser->waitUntilDisabled('.selector', 1);
Scoping Selectors When Available
때로는 주어진 셀렉터와 일치하는 요소가 나타날 때까지 기다린 다음 해당 요소와 상호작용하고 싶을 수 있습니다. 예를 들어 모달 창을 사용할 수 있을 때까지 기다린 뒤 모달 안의 "OK" 버튼을 누르고 싶을 수 있습니다. 이때 whenAvailable 메서드를 사용할 수 있습니다. 주어진 클로저 안에서 수행되는 모든 요소 작업은 원래 셀렉터로 스코프가 제한됩니다.
$browser->whenAvailable('.modal', function ($modal) {
$modal->assertSee('Hello World')
->press('OK');
});
Waiting For Text
waitForText 메서드는 주어진 텍스트가 페이지에 표시될 때까지 기다리는 데 사용할 수 있습니다.
// Wait a maximum of five seconds for the text...
$browser->waitForText('Hello World');
// Wait a maximum of one second for the text...
$browser->waitForText('Hello World', 1);
waitUntilMissingText 메서드를 사용하면 표시된 텍스트가 페이지에서 제거될 때까지 기다릴 수 있습니다.
// Wait a maximum of five seconds for the text to be removed...
$browser->waitUntilMissingText('Hello World');
// Wait a maximum of one second for the text to be removed...
$browser->waitUntilMissingText('Hello World', 1);
Waiting For Links
waitForLink 메서드는 주어진 링크 텍스트가 페이지에 표시될 때까지 기다리는 데 사용할 수 있습니다.
// Wait a maximum of five seconds for the link...
$browser->waitForLink('Create');
// Wait a maximum of one second for the link...
$browser->waitForLink('Create', 1);
Waiting On The Page Location
$browser->assertPathIs('/home')처럼 경로 어서션을 수행할 때 window.location.pathname이 비동기적으로 업데이트되고 있다면 어서션이 실패할 수 있습니다. waitForLocation 메서드를 사용하면 위치가 주어진 값이 될 때까지 기다릴 수 있습니다.
$browser->waitForLocation('/secret');
waitForLocation 메서드는 현재 창 위치가 정규화된 전체 URL이 될 때까지 기다리는 데도 사용할 수 있습니다.
$browser->waitForLocation('https://example.com/path');
named route's의 위치를 기다릴 수도 있습니다.
$browser->waitForRoute($routeName, $parameters);
Waiting for Page Reloads
작업을 수행한 뒤 페이지가 새로고침될 때까지 기다려야 한다면 waitForReload 메서드를 사용합니다.
use Laravel\Dusk\Browser;
$browser->waitForReload(function (Browser $browser) {
$browser->press('Submit');
})
->assertSee('Success!');
페이지 새로고침을 기다려야 하는 상황은 보통 버튼을 클릭한 뒤에 발생하므로, 편의를 위해 clickAndWaitForReload 메서드를 사용할 수 있습니다.
$browser->clickAndWaitForReload('.selector')
->assertSee('something');
Waiting On JavaScript Expressions
때로는 주어진 JavaScript 표현식이 true로 평가될 때까지 테스트 실행을 멈추고 싶을 수 있습니다. waitUntil 메서드를 사용하면 이를 쉽게 처리할 수 있습니다. 이 메서드에 표현식을 전달할 때는 return 키워드나 끝 세미콜론을 포함하지 않아도 됩니다.
// Wait a maximum of five seconds for the expression to be true...
$browser->waitUntil('App.data.servers.length > 0');
// Wait a maximum of one second for the expression to be true...
$browser->waitUntil('App.data.servers.length > 0', 1);
Waiting On Vue Expressions
waitUntilVue와 waitUntilVueIsNot 메서드는 Vue component의 속성이 주어진 값을 가질 때까지 기다리는 데 사용할 수 있습니다.
// Wait until the component attribute contains the given value...
$browser->waitUntilVue('user.name', 'Taylor', '@user');
// Wait until the component attribute doesn't contain the given value...
$browser->waitUntilVueIsNot('user.name', null, '@user');
Waiting With A Callback
Dusk의 여러 "wait" 메서드는 내부적으로 waitUsing 메서드에 의존합니다. 이 메서드를 직접 사용해 주어진 클로저가 true를 반환할 때까지 기다릴 수 있습니다. waitUsing 메서드는 기다릴 최대 초 수, 클로저를 평가할 간격, 클로저, 그리고 선택 사항인 실패 메시지를 받습니다.
$browser->waitUsing(10, 1, function () use ($something) {
return $something->isReady();
}, "Something wasn't ready in time.");
Scrolling An Element Into View
때로는 요소가 브라우저의 보이는 영역 밖에 있어 클릭할 수 없을 수 있습니다. scrollIntoView 메서드는 주어진 셀렉터의 요소가 보이는 영역 안에 들어올 때까지 브라우저 창을 스크롤합니다.
$browser->scrollIntoView('.selector')
->click('.selector');
Available Assertions
Dusk는 애플리케이션에 대해 수행할 수 있는 다양한 어서션을 제공합니다. 사용할 수 있는 모든 어서션은 아래 목록에 문서화되어 있습니다.
assertTitle assertTitleContains assertUrlIs assertSchemeIs assertSchemeIsNot assertHostIs assertHostIsNot assertPortIs assertPortIsNot assertPathBeginsWith assertPathIs assertPathIsNot assertRouteIs assertQueryStringHas assertQueryStringMissing assertFragmentIs assertFragmentBeginsWith assertFragmentIsNot assertHasCookie assertHasPlainCookie assertCookieMissing assertPlainCookieMissing assertCookieValue assertPlainCookieValue assertSee assertDontSee assertSeeIn assertDontSeeIn assertSeeAnythingIn assertSeeNothingIn assertScript assertSourceHas assertSourceMissing assertSeeLink assertDontSeeLink assertInputValue assertInputValueIsNot assertChecked assertNotChecked assertRadioSelected assertRadioNotSelected assertSelected assertNotSelected assertSelectHasOptions assertSelectMissingOptions assertSelectHasOption assertSelectMissingOption assertValue assertValueIsNot assertAttribute assertAttributeContains assertAriaAttribute assertDataAttribute assertVisible assertPresent assertNotPresent assertMissing assertInputPresent assertInputMissing assertDialogOpened assertEnabled assertDisabled assertButtonEnabled assertButtonDisabled assertFocused assertNotFocused assertAuthenticated assertGuest assertAuthenticatedAs assertVue assertVueIsNot assertVueContains assertVueDoesNotContain
assertTitle
페이지 제목이 주어진 텍스트와 일치하는지 어서션합니다.
$browser->assertTitle($title);
assertTitleContains
페이지 제목에 주어진 텍스트가 포함되어 있는지 어서션합니다.
$browser->assertTitleContains($title);
assertUrlIs
현재 URL(쿼리 문자열 제외)이 주어진 문자열과 일치하는지 어서션합니다.
$browser->assertUrlIs($url);
assertSchemeIs
현재 URL 스키마가 주어진 스키마와 일치하는지 어서션합니다.
$browser->assertSchemeIs($scheme);
assertSchemeIsNot
현재 URL 스키마가 주어진 스키마와 일치하지 않는지 어서션합니다.
$browser->assertSchemeIsNot($scheme);
assertHostIs
현재 URL 호스트가 주어진 호스트와 일치하는지 어서션합니다.
$browser->assertHostIs($host);
assertHostIsNot
현재 URL 호스트가 주어진 호스트와 일치하지 않는지 어서션합니다.
$browser->assertHostIsNot($host);
assertPortIs
현재 URL 포트가 주어진 포트와 일치하는지 어서션합니다.
$browser->assertPortIs($port);
assertPortIsNot
현재 URL 포트가 주어진 포트와 일치하지 않는다고 단언합니다:
$browser->assertPortIsNot($port);
assertPathBeginsWith
현재 URL 경로가 주어진 경로로 시작한다고 단언합니다:
$browser->assertPathBeginsWith('/home');
assertPathIs
현재 경로가 주어진 경로와 일치한다고 단언합니다:
$browser->assertPathIs('/home');
assertPathIsNot
현재 경로가 주어진 경로와 일치하지 않는다고 단언합니다:
$browser->assertPathIsNot('/home');
assertRouteIs
현재 URL이 주어진 named route's의 URL과 일치한다고 단언합니다:
$browser->assertRouteIs($name, $parameters);
assertQueryStringHas
주어진 쿼리 문자열 파라미터가 있다고 단언합니다:
$browser->assertQueryStringHas($name);
주어진 쿼리 문자열 파라미터가 있으며 주어진 값을 가진다고 단언합니다:
$browser->assertQueryStringHas($name, $value);
assertQueryStringMissing
주어진 쿼리 문자열 파라미터가 없다고 단언합니다:
$browser->assertQueryStringMissing($name);
assertFragmentIs
URL의 현재 해시 프래그먼트가 주어진 프래그먼트와 일치한다고 단언합니다:
$browser->assertFragmentIs('anchor');
assertFragmentBeginsWith
URL의 현재 해시 프래그먼트가 주어진 프래그먼트로 시작한다고 단언합니다:
$browser->assertFragmentBeginsWith('anchor');
assertFragmentIsNot
URL의 현재 해시 프래그먼트가 주어진 프래그먼트와 일치하지 않는다고 단언합니다:
$browser->assertFragmentIsNot('anchor');
assertHasCookie
주어진 암호화된 쿠키가 있다고 단언합니다:
$browser->assertHasCookie($name);
assertHasPlainCookie
주어진 암호화되지 않은 쿠키가 있다고 단언합니다:
$browser->assertHasPlainCookie($name);
assertCookieMissing
주어진 암호화된 쿠키가 없다고 단언합니다:
$browser->assertCookieMissing($name);
assertPlainCookieMissing
주어진 암호화되지 않은 쿠키가 없다고 단언합니다:
$browser->assertPlainCookieMissing($name);
assertCookieValue
암호화된 쿠키가 주어진 값을 가진다고 단언합니다:
$browser->assertCookieValue($name, $value);
assertPlainCookieValue
암호화되지 않은 쿠키가 주어진 값을 가진다고 단언합니다:
$browser->assertPlainCookieValue($name, $value);
assertSee
주어진 텍스트가 페이지에 있다고 단언합니다:
$browser->assertSee($text);
assertDontSee
주어진 텍스트가 페이지에 없다고 단언합니다:
$browser->assertDontSee($text);
assertSeeIn
주어진 텍스트가 셀렉터 안에 있다고 단언합니다:
$browser->assertSeeIn($selector, $text);
assertDontSeeIn
주어진 텍스트가 셀렉터 안에 없다고 단언합니다:
$browser->assertDontSeeIn($selector, $text);
assertSeeAnythingIn
셀렉터 안에 어떤 텍스트든 있다고 단언합니다:
$browser->assertSeeAnythingIn($selector);
assertSeeNothingIn
셀렉터 안에 텍스트가 없다고 단언합니다:
$browser->assertSeeNothingIn($selector);
assertScript
주어진 JavaScript 표현식이 주어진 값으로 평가된다고 단언합니다:
$browser->assertScript('window.isLoaded')
->assertScript('document.readyState', 'complete');
assertSourceHas
주어진 소스 코드가 페이지에 있다고 단언합니다:
$browser->assertSourceHas($code);
assertSourceMissing
주어진 소스 코드가 페이지에 없다고 단언합니다:
$browser->assertSourceMissing($code);
assertSeeLink
주어진 링크가 페이지에 있다고 단언합니다:
$browser->assertSeeLink($linkText);
assertDontSeeLink
주어진 링크가 페이지에 없다고 단언합니다:
$browser->assertDontSeeLink($linkText);
assertInputValue
주어진 input 필드가 주어진 값을 가진다고 단언합니다:
$browser->assertInputValue($field, $value);
assertInputValueIsNot
주어진 input 필드가 주어진 값을 가지지 않는다고 단언합니다:
$browser->assertInputValueIsNot($field, $value);
assertChecked
주어진 체크박스가 체크되어 있다고 단언합니다:
$browser->assertChecked($field);
assertNotChecked
주어진 체크박스가 체크되어 있지 않다고 단언합니다:
$browser->assertNotChecked($field);
assertRadioSelected
주어진 라디오 필드가 선택되어 있다고 단언합니다:
$browser->assertRadioSelected($field, $value);
assertRadioNotSelected
주어진 라디오 필드가 선택되어 있지 않다고 단언합니다:
$browser->assertRadioNotSelected($field, $value);
assertSelected
주어진 드롭다운에서 주어진 값이 선택되어 있다고 단언합니다:
$browser->assertSelected($field, $value);
assertNotSelected
주어진 드롭다운에서 주어진 값이 선택되어 있지 않다고 단언합니다:
$browser->assertNotSelected($field, $value);
assertSelectHasOptions
주어진 값 배열을 선택할 수 있다고 단언합니다:
$browser->assertSelectHasOptions($field, $values);
assertSelectMissingOptions
주어진 값 배열을 선택할 수 없다고 단언합니다:
$browser->assertSelectMissingOptions($field, $values);
assertSelectHasOption
주어진 필드에서 주어진 값을 선택할 수 있다고 단언합니다:
$browser->assertSelectHasOption($field, $value);
assertSelectMissingOption
주어진 값을 선택할 수 없다고 단언합니다:
$browser->assertSelectMissingOption($field, $value);
assertValue
주어진 셀렉터와 일치하는 요소가 주어진 값을 가진다고 단언합니다:
$browser->assertValue($selector, $value);
assertValueIsNot
주어진 셀렉터와 일치하는 요소가 주어진 값을 가지지 않는다고 단언합니다:
$browser->assertValueIsNot($selector, $value);
assertAttribute
주어진 셀렉터와 일치하는 요소의 지정된 속성에 주어진 값이 있다고 단언합니다:
$browser->assertAttribute($selector, $attribute, $value);
assertAttributeContains
주어진 셀렉터와 일치하는 요소의 지정된 속성에 주어진 값이 포함되어 있다고 단언합니다:
$browser->assertAttributeContains($selector, $attribute, $value);
assertAriaAttribute
주어진 셀렉터와 일치하는 요소의 지정된 aria 속성에 주어진 값이 있다고 단언합니다:
$browser->assertAriaAttribute($selector, $attribute, $value);
예를 들어 <button aria-label="Add"></button> 마크업이 있다면, 다음처럼 aria-label 속성에 대해 어설션할 수 있습니다.
$browser->assertAriaAttribute('button', 'label', 'Add')
assertDataAttribute
주어진 셀렉터와 일치하는 요소의 지정된 데이터 속성에 주어진 값이 있는지 어설션합니다.
$browser->assertDataAttribute($selector, $attribute, $value);
예를 들어 <tr id="row-1" data-content="attendees"></tr> 마크업이 있다면, 다음처럼 data-label 속성에 대해 어설션할 수 있습니다.
$browser->assertDataAttribute('#row-1', 'content', 'attendees')
assertVisible
주어진 셀렉터와 일치하는 요소가 보이는지 어설션합니다.
$browser->assertVisible($selector);
assertPresent
주어진 셀렉터와 일치하는 요소가 소스에 있는지 어설션합니다.
$browser->assertPresent($selector);
assertNotPresent
주어진 셀렉터와 일치하는 요소가 소스에 없는지 어설션합니다.
$browser->assertNotPresent($selector);
assertMissing
주어진 셀렉터와 일치하는 요소가 보이지 않는지 어설션합니다.
$browser->assertMissing($selector);
assertInputPresent
주어진 이름의 input이 있는지 어설션합니다.
$browser->assertInputPresent($name);
assertInputMissing
주어진 이름의 input이 소스에 없는지 어설션합니다.
$browser->assertInputMissing($name);
assertDialogOpened
주어진 메시지를 가진 JavaScript 다이얼로그가 열렸는지 어설션합니다.
$browser->assertDialogOpened($message);
assertEnabled
주어진 필드가 활성화되어 있는지 어설션합니다.
$browser->assertEnabled($field);
assertDisabled
주어진 필드가 비활성화되어 있는지 어설션합니다.
$browser->assertDisabled($field);
assertButtonEnabled
주어진 버튼이 활성화되어 있는지 어설션합니다.
$browser->assertButtonEnabled($button);
assertButtonDisabled
주어진 버튼이 비활성화되어 있는지 어설션합니다.
$browser->assertButtonDisabled($button);
assertFocused
주어진 필드에 포커스가 있는지 어설션합니다.
$browser->assertFocused($field);
assertNotFocused
주어진 필드에 포커스가 없는지 어설션합니다.
$browser->assertNotFocused($field);
assertAuthenticated
사용자가 인증되었는지 어설션합니다.
$browser->assertAuthenticated();
assertGuest
사용자가 인증되지 않았는지 어설션합니다.
$browser->assertGuest();
assertAuthenticatedAs
사용자가 주어진 사용자로 인증되었는지 어설션합니다.
$browser->assertAuthenticatedAs($user);
assertVue
Dusk에서는 Vue component 데이터의 상태에 대해서도 어설션할 수 있습니다. 예를 들어 애플리케이션에 다음 Vue 컴포넌트가 있다고 가정해 보겠습니다.
// HTML...
<profile dusk="profile-component"></profile>
// Component Definition...
Vue.component('profile', {
template: '<div>{{ user.name }}</div>',
data: function () {
return {
user: {
name: 'Taylor'
}
};
}
});
다음처럼 Vue 컴포넌트의 상태에 대해 어설션할 수 있습니다.
/**
* A basic Vue test example.
*
* @return void
*/
public function testVue()
{
$this->browse(function (Browser $browser) {
$browser->visit('/')
->assertVue('user.name', 'Taylor', '@profile-component');
});
}
assertVueIsNot
주어진 Vue 컴포넌트 데이터 프로퍼티가 주어진 값과 일치하지 않는지 어설션합니다.
$browser->assertVueIsNot($property, $value, $componentSelector = null);
assertVueContains
주어진 Vue 컴포넌트 데이터 프로퍼티가 배열이며 주어진 값을 포함하는지 어설션합니다.
$browser->assertVueContains($property, $value, $componentSelector = null);
assertVueDoesNotContain
주어진 Vue 컴포넌트 데이터 프로퍼티가 배열이며 주어진 값을 포함하지 않는지 어설션합니다.
$browser->assertVueDoesNotContain($property, $value, $componentSelector = null);
Pages
때로는 테스트에서 복잡한 여러 동작을 순서대로 수행해야 합니다. 그러면 테스트를 읽고 이해하기가 더 어려워질 수 있습니다. Dusk 페이지를 사용하면 주어진 페이지에서 단일 메서드로 실행할 수 있는 표현력 있는 동작을 정의할 수 있습니다. 페이지에서는 애플리케이션 전체 또는 단일 페이지에서 자주 쓰는 셀렉터의 단축 표현도 정의할 수 있습니다.
Generating Pages
페이지 객체를 생성하려면 dusk:page Artisan 명령어를 실행합니다. 모든 페이지 객체는 애플리케이션의 tests/Browser/Pages 디렉터리에 배치됩니다.
php artisan dusk:page Login
Configuring Pages
기본적으로 페이지에는 url, assert, elements 세 가지 메서드가 있습니다. 여기서는 url과 assert 메서드를 살펴보겠습니다. elements 메서드는 discussed in more detail below.
The url Method
url 메서드는 페이지를 나타내는 URL의 경로를 반환해야 합니다. Dusk는 브라우저에서 해당 페이지로 이동할 때 이 URL을 사용합니다.
/**
* Get the URL for the page.
*
* @return string
*/
public function url()
{
return '/login';
}
The assert Method
assert 메서드에서는 브라우저가 실제로 주어진 페이지에 있는지 확인하는 데 필요한 어설션을 수행할 수 있습니다. 이 메서드 안에 반드시 무언가를 작성해야 하는 것은 아닙니다. 다만 원한다면 자유롭게 이러한 어설션을 추가할 수 있습니다. 이 어설션들은 페이지로 이동할 때 자동으로 실행됩니다.
/**
* Assert that the browser is on the page.
*
* @return void
*/
public function assert(Browser $browser)
{
$browser->assertPathIs($this->url());
}
Navigating To Pages
페이지를 정의한 뒤에는 visit 메서드로 해당 페이지로 이동할 수 있습니다.
use Tests\Browser\Pages\Login;
$browser->visit(new Login);
때로는 이미 주어진 페이지에 있는 상태에서, 그 페이지의 셀렉터와 메서드를 현재 테스트 컨텍스트로 "로드"해야 할 수 있습니다. 버튼을 눌러 명시적으로 이동하지 않고 특정 페이지로 리디렉션되는 경우에 흔히 발생합니다. 이런 상황에서는 on 메서드로 페이지를 로드할 수 있습니다.
use Tests\Browser\Pages\CreatePlaylist;
$browser->visit('/dashboard')
->clickLink('Create Playlist')
->on(new CreatePlaylist)
->assertSee('@create');
Shorthand Selectors
페이지 클래스의 elements 메서드에서는 페이지의 CSS 셀렉터에 대해 빠르고 기억하기 쉬운 단축 표현을 정의할 수 있습니다. 예를 들어 애플리케이션 로그인 페이지의 "email" input 필드에 대한 단축 표현을 정의해 보겠습니다.
/**
* Get the element shortcuts for the page.
*
* @return array
*/
public function elements()
{
return [
'@email' => 'input[name=email]',
];
}
단축 표현을 정의한 뒤에는 일반적으로 전체 CSS 셀렉터를 사용할 곳 어디에서나 단축 셀렉터를 사용할 수 있습니다.
Global Shorthand Selectors
Dusk를 설치하면 기본 Page 클래스가 tests/Browser/Pages 디렉터리에 배치됩니다. 이 클래스에는 애플리케이션의 모든 페이지에서 사용할 수 있어야 하는 전역 단축 셀렉터를 정의하는 데 쓸 수 있는 siteElements 메서드가 있습니다.
/**
* Get the global element shortcuts for the site.
*
* @return array
*/
public static function siteElements()
{
return [
'@element' => '#selector',
];
}
Page Methods
페이지에 정의된 기본 메서드 외에도 테스트 전반에서 사용할 추가 메서드를 정의할 수 있습니다. 예를 들어 음악 관리 애플리케이션을 만든다고 가정해 보겠습니다. 애플리케이션의 한 페이지에서 자주 수행하는 동작은 플레이리스트를 만드는 일일 수 있습니다. 각 테스트마다 플레이리스트 생성 로직을 다시 작성하는 대신, 페이지 클래스에 createPlaylist 메서드를 정의할 수 있습니다.
<?php
namespace Tests\Browser\Pages;
use Laravel\Dusk\Browser;
class Dashboard extends Page
{
// Other page methods...
/**
* Create a new playlist.
*
* @param \Laravel\Dusk\Browser $browser
* @param string $name
* @return void
*/
public function createPlaylist(Browser $browser, $name)
{
$browser->type('name', $name)
->check('share')
->press('Create Playlist');
}
}
메서드를 정의한 뒤에는 해당 페이지를 사용하는 모든 테스트에서 이 메서드를 사용할 수 있습니다. 브라우저 인스턴스는 커스텀 페이지 메서드의 첫 번째 인수로 자동 전달됩니다.
use Tests\Browser\Pages\Dashboard;
$browser->visit(new Dashboard)
->createPlaylist('My Playlist')
->assertSee('My Playlist');
Components
컴포넌트는 Dusk의 “페이지 객체”와 비슷하지만, 내비게이션 바나 알림 창처럼 애플리케이션 전반에서 재사용되는 UI와 기능 조각을 대상으로 합니다. 따라서 컴포넌트는 특정 URL에 묶이지 않습니다.
Generating Components
컴포넌트를 생성하려면 dusk:component Artisan 명령어를 실행합니다. 새 컴포넌트는 tests/Browser/Components 디렉터리에 배치됩니다:
php artisan dusk:component DatePicker
위에서 보듯이 "date picker"는 애플리케이션의 여러 페이지 전반에 존재할 수 있는 컴포넌트의 예입니다. 테스트 스위트 곳곳에 있는 수십 개의 테스트에서 날짜를 선택하는 브라우저 자동화 로직을 직접 작성하면 번거로워질 수 있습니다. 대신 date picker를 나타내는 Dusk 컴포넌트를 정의해 해당 로직을 컴포넌트 안에 캡슐화할 수 있습니다:
<?php
namespace Tests\Browser\Components;
use Laravel\Dusk\Browser;
use Laravel\Dusk\Component as BaseComponent;
class DatePicker extends BaseComponent
{
/**
* Get the root selector for the component.
*
* @return string
*/
public function selector()
{
return '.date-picker';
}
/**
* Assert that the browser page contains the component.
*
* @param Browser $browser
* @return void
*/
public function assert(Browser $browser)
{
$browser->assertVisible($this->selector());
}
/**
* Get the element shortcuts for the component.
*
* @return array
*/
public function elements()
{
return [
'@date-field' => 'input.datepicker-input',
'@year-list' => 'div > div.datepicker-years',
'@month-list' => 'div > div.datepicker-months',
'@day-list' => 'div > div.datepicker-days',
];
}
/**
* Select the given date.
*
* @param \Laravel\Dusk\Browser $browser
* @param int $year
* @param int $month
* @param int $day
* @return void
*/
public function selectDate(Browser $browser, $year, $month, $day)
{
$browser->click('@date-field')
->within('@year-list', function ($browser) use ($year) {
$browser->click($year);
})
->within('@month-list', function ($browser) use ($month) {
$browser->click($month);
})
->within('@day-list', function ($browser) use ($day) {
$browser->click($day);
});
}
}
Using Components
컴포넌트를 정의하고 나면 어떤 테스트에서든 date picker 안의 날짜를 쉽게 선택할 수 있습니다. 날짜 선택에 필요한 로직이 바뀌더라도 컴포넌트만 업데이트하면 됩니다:
<?php
namespace Tests\Browser;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Laravel\Dusk\Browser;
use Tests\Browser\Components\DatePicker;
use Tests\DuskTestCase;
class ExampleTest extends DuskTestCase
{
/**
* A basic component test example.
*
* @return void
*/
public function testBasicExample()
{
$this->browse(function (Browser $browser) {
$browser->visit('/')
->within(new DatePicker, function ($browser) {
$browser->selectDate(2019, 1, 30);
})
->assertSee('January');
});
}
}
Continuous Integration
대부분의 Dusk 지속적 통합 설정은 Laravel 애플리케이션이 포트 8000에서 PHP 내장 개발 서버로 제공된다고 가정합니다. 따라서 계속하기 전에 지속적 통합 환경의
APP_URL환경 변수 값이http://127.0.0.1:8000인지 확인해야 합니다.
Heroku CI
Heroku CI에서 Dusk 테스트를 실행하려면 다음 Google Chrome buildpack과 스크립트를 Heroku app.json 파일에 추가합니다:
{
"environments": {
"test": {
"buildpacks": [
{ "url": "heroku/php" },
{ "url": "https://github.com/heroku/heroku-buildpack-google-chrome" }
],
"scripts": {
"test-setup": "cp .env.testing .env",
"test": "nohup bash -c './vendor/laravel/dusk/bin/chromedriver-linux > /dev/null 2>&1 &' && nohup bash -c 'php artisan serve --no-reload > /dev/null 2>&1 &' && php artisan dusk"
}
}
}
}
Travis CI
Travis CI에서 Dusk 테스트를 실행하려면 다음 .travis.yml 설정을 사용합니다. Travis CI는 그래픽 환경이 아니므로 Chrome 브라우저를 실행하려면 몇 가지 추가 단계가 필요합니다. 또 PHP 내장 웹 서버를 실행하기 위해 php artisan serve를 사용합니다:
language: php
php:
- 7.3
addons:
chrome: stable
install:
- cp .env.testing .env
- travis_retry composer install --no-interaction --prefer-dist
- php artisan key:generate
- php artisan dusk:chrome-driver
before_script:
- google-chrome-stable --headless --disable-gpu --remote-debugging-port=9222 http://localhost &
- php artisan serve --no-reload &
script:
- php artisan dusk
GitHub Actions
Github Actions를 사용해 Dusk 테스트를 실행한다면 다음 설정 파일을 시작점으로 사용할 수 있습니다. TravisCI와 마찬가지로 PHP 내장 웹 서버를 실행하기 위해 php artisan serve 명령어를 사용합니다:
name: CI
on: [push]
jobs:
dusk-php:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Prepare The Environment
run: cp .env.example .env
- name: Create Database
run: |
sudo systemctl start mysql
mysql --user="root" --password="root" -e "CREATE DATABASE 'my-database' character set UTF8mb4 collate utf8mb4_bin;"
- name: Install Composer Dependencies
run: composer install --no-progress --prefer-dist --optimize-autoloader
- name: Generate Application Key
run: php artisan key:generate
- name: Upgrade Chrome Driver
run: php artisan dusk:chrome-driver `/opt/google/chrome/chrome --version | cut -d " " -f3 | cut -d "." -f1`
- name: Start Chrome Driver
run: ./vendor/laravel/dusk/bin/chromedriver-linux &
- name: Run Laravel Server
run: php artisan serve --no-reload &
- name: Run Dusk Tests
env:
APP_URL: "http://127.0.0.1:8000"
run: php artisan dusk
- name: Upload Screenshots
if: failure()
uses: actions/upload-artifact@v2
with:
name: screenshots
path: tests/Browser/screenshots
- name: Upload Console Logs
if: failure()
uses: actions/upload-artifact@v2
with:
name: console
path: tests/Browser/console