mc caulay/duskless

Laravel Duskless 提供浏览器自动化。

dev-master 2021-01-19 20:29 UTC

This package is auto-updated.

Last update: 2024-09-20 04:52:46 UTC


README

Total Downloads Latest Stable Version License

Laravel Duskless

简介

Laravel Duskless 提供了一个表达性、易于使用的浏览器自动化 API。默认情况下,Duskless 不需要你在机器上安装 JDK 或 Selenium。相反,Duskless 使用一个独立的 ChromeDriver 安装。但是,你可以自由地使用任何其他你希望使用的 Selenium 兼容驱动程序。

安装

要开始,你应该将 mccaulay/duskless Composer 依赖项添加到你的项目中

composer require mccaulay/duskless

{note} 你可以在生产环境中注册 Duskless,因为防止你这样做的问题都已经解决。例如,loginAs 方法。

安装 Duskless 包后,运行 duskless:install Artisan 命令

php artisan duskless:install

app 目录中创建一个 Browser 目录,其中包含一个示例页面。接下来,在 .env 文件中设置 APP_URL 环境变量。此值应与你在浏览器中访问应用程序时使用的 URL 匹配。

管理 ChromeDriver 安装

如果您想要安装 Laravel Duskless 中包含的以外的不同版本的 ChromeDriver,您可以使用 duskless:chrome-driver 命令

# Install the latest version of ChromeDriver for your OS...
php artisan duskless:chrome-driver

# Install a given version of ChromeDriver for your OS...
php artisan duskless:chrome-driver 74

# Install a given version of ChromeDriver for all supported OSs...
php artisan duskless:chrome-driver --all

{note} Duskless 需要 chromedriver 二进制文件是可执行的。如果您在运行 Duskless 时遇到问题,请确保二进制文件是可执行的,使用以下命令:chmod -R 0755 vendor/laravel/dusk/bin/

入门

创建浏览器

要开始,让我们编写一个示例控制器,访问 github 存储库。要创建浏览器实例,调用 browse 方法

    <?php

    namespace App\Http\Controllers;

    use Illuminate\Http\Request;
    use McCaulay\Duskless\Duskless;

    class ExampleController extends Controller
    {
        /**
        * Browse a website example.
        *
        * @return \Illuminate\Http\Response
        */
        public function example()
        {
            $duskless = new Duskless();

            // Set the window size to 1080p
            $duskless->windowSize(1920, 1080);

            // Set headless and without gpu
            // $duskless->headless()->disableGpu()->noSandbox();

            // Set user agent
            $duskless->userAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36');

            // Start the browser
            $duskless->start();

            // Launch a browser
            $duskless->browse(function ($browser) {

                // Visit web page
                $browser->visit('https://github.com/McCaulay/duskless');
            });

            return view('example.index');
        }
    }

如上例所示,browse 方法接受一个回调。Duskless 将自动将浏览器实例传递给此回调,并是用于与您的应用程序交互和进行断言的主要对象。

创建多个浏览器

在自动化浏览器时,有时可能需要多个浏览器。例如,在两个屏幕之间进行聊天时可能需要多个浏览器,这两个屏幕通过 websockets 交互。要创建多个浏览器,在 browse 方法的回调签名中“请求”多个浏览器。

    $this->browse(function ($first, $second) {
        $first->visit('/home')
              ->waitForText('Message');

        $second->visit('/home')
               ->waitForText('Message')
               ->type('message', 'Hey McCaulay')
               ->press('Send');

        $first->waitForText('Hey McCaulay');
    });

调整浏览器窗口大小

您可以使用 resize 方法调整浏览器窗口的大小

    $browser->resize(1920, 1080);

您可以使用 maximize 方法最大化浏览器窗口

    $browser->maximize();

与元素交互

Duskless 选择器

要定义选择器,请向您的 HTML 元素添加一个 dusk 属性。然后,在选择器前面加上 @ 以操作附加的元素

    // HTML...

    <button dusk="login-button">Login</button>

    // Test...

    $browser->click('@login-button');

点击链接

要点击链接,您可以在浏览器实例上使用 clickLink 方法。该 clickLink 方法将点击具有给定显示文本的链接

    $browser->clickLink($linkText);

{note} 此方法与 jQuery 交互。如果页面上没有 jQuery,Duskless 将自动将其注入页面,以便在测试期间可用。

文本、值和属性

检索和设置值

无暗光提供了多种方法来与页面上的当前元素文本、值和属性进行交互。例如,要获取匹配给定选择器的元素的“值”,请使用 value 方法

    // Retrieve the value...
    $value = $browser->value('selector');

    // Set the value...
    $browser->value('selector', 'value');

检索文本

可以使用 text 方法检索匹配给定选择器的元素的显示文本

    $text = $browser->text('selector');

检索属性

最后,可以使用 attribute 方法检索匹配给定选择器的元素的属性

    $attribute = $browser->attribute('selector', 'value');

使用表单

输入值

无暗光提供了一系列与表单和输入元素交互的方法。首先,让我们看看如何在输入字段中输入文本的示例

    $browser->type('email', 'taylor@laravel.com');

请注意,尽管该方法接受一个参数,但我们不必将 CSS 选择器传递给 type 方法。如果没有提供 CSS 选择器,无暗光将搜索具有给定 name 属性的输入字段。最后,无暗光将尝试找到具有给定 name 属性的 textarea

要向字段追加文本而不清除其内容,您可以使用 append 方法

    $browser->type('tags', 'foo')
            ->append('tags', ', bar, baz');

您可以使用 clear 方法清除输入的值

    $browser->clear('email');

下拉菜单

要选择下拉选择框中的值,请使用 select 方法。与 type 方法一样,select 方法不需要完整的 CSS 选择器。当传递值给 select 方法时,应传递底层选项值而不是显示文本

    $browser->select('size', 'Large');

您可以通过省略第二个参数来选择随机选项

    $browser->select('size');

复选框

要“勾选”复选框字段,请使用 check 方法。与许多其他输入相关方法一样,不需要完整的 CSS 选择器。如果无法找到精确的选择器匹配,无暗光将搜索具有匹配 name 属性的复选框

    $browser->check('terms');

    $browser->uncheck('terms');

单选按钮

要“选择”单选按钮选项,请使用 radio 方法。与许多其他输入相关方法一样,不需要完整的 CSS 选择器。如果无法找到精确的选择器匹配,无暗光将搜索具有匹配 namevalue 属性的单选按钮

    $browser->radio('version', 'php7');

附件文件

可以使用 attach 方法将文件附加到 file 输入元素。与许多其他输入相关方法一样,不需要完整的 CSS 选择器。如果无法找到精确的选择器匹配,无暗光将搜索具有匹配 name 属性的文件输入

    $browser->attach('photo', __DIR__.'/photos/me.png');

{note} attach 函数需要您的服务器上安装并启用了 Zip PHP 扩展。

使用键盘

keys 方法允许您向给定元素提供比 type 方法通常允许的更复杂的输入序列。例如,您可以按住修饰键输入值。在这个例子中,在匹配给定选择器的元素中输入 taylor 时,将按住 shift 键。在输入 taylor 后,将输入 otwell 而不带任何修饰键

    $browser->keys('selector', ['{shift}', 'taylor'], 'otwell');

您甚至可以向包含您的应用程序的主要 CSS 选择器发送“热键”

    $browser->keys('.app', ['{command}', 'j']);

{tip} 所有修饰键都包含在 {} 字符中,并匹配在 Facebook\WebDriver\WebDriverKeys 类中定义的常量,可以在 GitHub 上找到。

使用鼠标

点击元素

可以使用 click 方法“点击”匹配给定选择器的元素

    $browser->click('.selector');

鼠标悬停

当您需要将鼠标悬停在匹配给定选择器的元素上时,请使用 mouseover 方法

    $browser->mouseover('.selector');

拖放

可以使用 drag 方法将匹配给定选择器的元素拖动到另一个元素

    $browser->drag('.from-selector', '.to-selector');

或者,您可以将元素拖动到单一方向

    $browser->dragLeft('.selector', 10);
    $browser->dragRight('.selector', 10);
    $browser->dragUp('.selector', 10);
    $browser->dragDown('.selector', 10);

JavaScript 对话框

无暗光提供了与 JavaScript 对话框交互的各种方法

    // Wait for a dialog to appear:
    $browser->waitForDialog($seconds = null);
    
    // Assert that a dialog has been displayed and that its message matches the given value:
    $browser->assertDialogOpened('value');

    // Type the given value in an open JavaScript prompt dialog:
    $browser->typeInDialog('Hello World');

要关闭打开的 JavaScript 对话框,请点击 OK 按钮

    $browser->acceptDialog();

要关闭已打开的JavaScript对话框,请点击取消按钮(仅适用于确认对话框)

    $browser->dismissDialog();

选择器作用域

有时您可能希望在给定的选择器内执行多个操作。例如,您可能只想在表格中断言某些文本存在,然后在该表格中点击按钮。您可以使用 with 方法来实现这一点。在 with 方法给出的回调中执行的所有操作都将限定在原始选择器内

    $browser->with('.table', function ($table) {
        $table->assertSee('Hello World')
              ->clickLink('Delete');
    });

等待元素

使用各种方法,您可以等待元素在页面上可见,或者等待给定的JavaScript表达式计算为 true

等待

如果您需要暂停测试一段时间(以毫秒为单位),请使用 pause 方法

    $browser->pause(1000);

等待选择器

可以使用 waitFor 方法在测试执行暂停,直到页面显示与给定CSS选择器匹配的元素。默认情况下,这将在抛出异常之前最多暂停五秒钟。如果需要,您可以向该方法传递自定义的超时阈值作为第二个参数

    // 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);

您也可以等待给定的选择器在页面上不存在

    $browser->waitUntilMissing('.selector');

    $browser->waitUntilMissing('.selector', 1);

当选择器可用时的作用域

有时,您可能希望等待给定的选择器然后与匹配选择器的元素进行交互。例如,您可能希望等待模态窗口可用,然后在该模态中按下“确定”按钮。在这种情况下,可以使用 whenAvailable 方法。在给定的回调中执行的所有元素操作都将限定在原始选择器内

    $browser->whenAvailable('.modal', function ($modal) {
        $modal->assertSee('Hello World')
              ->press('OK');
    });

等待文本

可以使用 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);

等待链接

可以使用 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);

等待页面位置

当进行路径断言,如 $browser->assertPathIs('/home') 时,如果 window.location.pathname 正在异步更新,断言可能会失败。您可以使用 waitForLocation 方法等待位置变为给定的值

    $browser->waitForLocation('/secret');

您也可以等待命名路由的位置

    $browser->waitForRoute($routeName, $parameters);

等待页面重新加载

如果需要在页面重新加载后等待,请使用 waitForReload 方法

    $browser->click('.some-action')
            ->waitForReload();

等待JavaScript表达式

有时您可能希望在给定的JavaScript表达式计算为 true 之前暂停脚本的执行。您可以使用 waitUntil 方法轻松地完成此操作。向此方法传递表达式时,您不需要包括 return 关键字或结束的分号

    // Wait a maximum of five seconds for the expression to be true...
    $browser->waitUntil('App.dataLoaded');

    $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);

等待Vue表达式

以下方法可用于等待给定的Vue组件属性具有给定的值

    // Wait until the component attribute contains the given value...
    $browser->waitUntilVue('user.name', 'McCaulay', '@user');

    // Wait until the component attribute doesn't contain the given value...
    $browser->waitUntilVueIsNot('user.name', null, '@user');

带有回调的等待

Duskless 中的许多“等待”方法依赖于底层的 waitUsing 方法。您可以直接使用此方法等待给定的回调返回 truewaitUsing 方法接受最大等待秒数、闭包评估的间隔、闭包以及可选的失败消息

    $browser->waitUsing(10, 1, function () use ($something) {
        return $something->isReady();
    }, "Something wasn't ready in time.");

页面

页面允许您定义可以在给定页面上使用单个方法执行的表达式动作。页面还允许您为您的应用程序或单个页面定义常见选择器的快捷方式。

生成页面

要生成页面对象,请使用 duskless:page Artisan 命令。所有页面对象都将放置在 app/Browser/Pages 目录中

php artisan duskless:page Login

配置页面

默认情况下,页面有两个方法:urlelements。我们现在将讨论 url 方法。下面将更详细地讨论 elements 方法。

《url》方法

url 方法应返回表示页面的 URL 的路径。Duskless 在浏览器中导航到页面时将使用此 URL

    /**
     * Get the URL for the page.
     *
     * @return string
     */
    public function url()
    {
        return '/login';
    }

导航到页面

一旦配置了页面,您可以使用 visit 方法导航到该页面

    use App\Browser\Pages\Login;

    $browser->visit(new Login);

有时您可能已经位于给定的页面,需要将页面选择器和方法“加载”到当前上下文中。在按下按钮并被重定向到给定页面而没有明确导航到该页面时,这很常见。在这种情况下,您可以使用 on 方法加载页面

    use App\Browser\Pages\CreatePlaylist;

    $browser->visit('/dashboard')
            ->clickLink('Create Playlist')
            ->on(new CreatePlaylist);

简写选择器

页面的 elements 方法允许您为页面上的任何 CSS 选择器定义快速、易于记忆的快捷方式。例如,让我们为应用程序登录页面的“电子邮件”输入字段定义一个快捷方式

    /**
     * Get the element shortcuts for the page.
     *
     * @return array
     */
    public function elements()
    {
        return [
            '@email' => 'input[name=email]',
        ];
    }

现在,您可以在需要使用完整 CSS 选择器的任何地方使用此简写选择器

    $browser->type('@email', 'taylor@laravel.com');

全局简写选择器

安装 Duskless 后,一个基本的 Page 类将被放置在您的 app/Browser/Pages 目录中。此类包含一个 siteElements 方法,可以用来定义应在您的应用程序中的每个页面上都可用的全局简写选择器

    /**
     * Get the global element shortcuts for the site.
     *
     * @return array
     */
    public static function siteElements()
    {
        return [
            '@element' => '#selector',
        ];
    }

页面方法

除了在页面上定义的默认方法外,您还可以定义其他方法,这些方法可以在整个脚本中使用。例如,让我们假设我们正在构建一个音乐管理应用程序。应用程序的一页上的常见操作可能是创建一个播放列表。您不必在每个测试中重新编写创建播放列表的逻辑,您可以在页面类上定义一个 createPlaylist 方法

    <?php

    namespace App\Browser\Pages;

    use McCaulay\Duskless\Browser;

    class Dashboard extends Page
    {
        // Other page methods...

        /**
         * Create a new playlist.
         *
         * @param  \McCaulay\Duskless\Browser  $browser
         * @param  string  $name
         * @return void
         */
        public function createPlaylist(Browser $browser, $name)
        {
            $browser->type('name', $name)
                    ->check('share')
                    ->press('Create Playlist');
        }
    }

一旦定义了方法,您就可以在任何使用页面的脚本中使用它。浏览器实例将自动传递到页面方法

    use App\Browser\Pages\Dashboard;

    $browser->visit(new Dashboard)
            ->createPlaylist('My Playlist')
            ->assertSee('My Playlist');

组件

组件类似于 Duskless 的“页面对象”,但旨在用于在整个应用程序中重复使用的 UI 和功能部分,例如导航栏或通知窗口。因此,组件不绑定到特定的 URL。

生成组件

要生成组件,请使用 duskless:component Artisan 命令。新组件将被放置在 app/Browser/Components 目录中

php artisan duskless:component DatePicker

如上图所示,“日期选择器”是可能在应用程序的多个页面上存在的组件的一个示例。手动在代码中的几十个脚本中编写浏览器自动化逻辑来选择日期可能会变得很繁琐。相反,我们可以定义一个 Duskless 组件来表示日期选择器,从而允许我们在组件中封装该逻辑

    <?php

    namespace App\Browser\Components;

    use McCaulay\Duskless\Browser;
    use McCaulay\Duskless\Component as BaseComponent;

    class DatePicker extends BaseComponent
    {
        /**
         * Get the root selector for the component.
         *
         * @return string
         */
        public function selector()
        {
            return '.date-picker';
        }

        /**
         * Get the element shortcuts for the component.
         *
         * @return array
         */
        public function elements()
        {
            return [
                '@date-field' => 'input.datepicker-input',
                '@month-list' => 'div > div.datepicker-months',
                '@day-list' => 'div > div.datepicker-days',
            ];
        }

        /**
         * Select the given date.
         *
         * @param  \McCaulay\Duskless\Browser  $browser
         * @param  int  $month
         * @param  int  $day
         * @return void
         */
        public function selectDate($browser, $month, $day)
        {
            $browser->click('@date-field')
                    ->within('@month-list', function ($browser) use ($month) {
                        $browser->click($month);
                    })
                    ->within('@day-list', function ($browser) use ($day) {
                        $browser->click($day);
                    });
        }
    }

使用组件

一旦定义了组件,我们就可以轻松地从任何测试中选择日期选择器中的日期。而且,如果选择日期所需的逻辑发生变化,我们只需要更新组件

$this->browse(function (Browser $browser) {
    $browser->visit('/')
        ->within(new DatePicker, function ($browser) {
            $browser->selectDate(1, 2018);
        });
});

许可证

Laravel Duskless 是开源软件,根据 MIT 许可证 许可。