浏览代码

FormHasManyTest

jqh 5 年之前
父节点
当前提交
1dc9588896

+ 59 - 0
tests/Browser.php

@@ -0,0 +1,59 @@
+<?php
+
+namespace Tests;
+
+use Laravel\Dusk\Browser as BaseBrowser;
+use Laravel\Dusk\Component;
+use Laravel\Dusk\ElementResolver;
+
+class Browser extends BaseBrowser
+{
+    /**
+     * @var static
+     */
+    public $parent;
+
+    /**
+     * 作用与 with 方法完全相同,不同的在于此方法可以让下层 Broser 对象继承当前 Component 的方法.
+     *
+     * @param string|Component $selector
+     * @param \Closure         $callback
+     *
+     * @return $this
+     */
+    public function extend($selector, $callback)
+    {
+        $browser = new static(
+            $this->driver, new ElementResolver($this->driver, $this->resolver->format($selector))
+        );
+
+        $browser->parent = $this;
+
+        if ($this->page) {
+            $browser->onWithoutAssert($this->page);
+        }
+
+        if ($selector instanceof Component) {
+            $browser->onComponent($selector, $this->resolver);
+        }
+
+        call_user_func($callback, $browser);
+
+        return $this;
+    }
+
+    public function __call($method, $parameters)
+    {
+        $parentComponent = $this->parent ? $this->parent->component : null;
+
+        if ($parentComponent && method_exists($parentComponent, $method)) {
+            array_unshift($parameters, $this);
+
+            $parentComponent->{$method}(...$parameters);
+
+            return $this;
+        }
+
+        return parent::__call($method, $parameters);
+    }
+}

+ 1 - 1
tests/Browser/AuthTest.php → tests/Browser/Cases/AuthTest.php

@@ -1,6 +1,6 @@
 <?php
 
-namespace Tests\Browser;
+namespace Tests\Browser\Cases;
 
 use Laravel\Dusk\Browser;
 use Tests\TestCase;

+ 122 - 0
tests/Browser/Cases/FormHasManyTest.php

@@ -0,0 +1,122 @@
+<?php
+
+namespace Tests\Browser\Cases;
+
+use Laravel\Dusk\Browser;
+use Tests\Browser\Pages\PainterCreatePage;
+use Tests\Browser\Pages\PainterEditPage;
+use Tests\Models\Painter;
+use Tests\Models\Painting;
+use Tests\TestCase;
+
+/**
+ * 一对多表单功能测试.
+ *
+ * @group form-has-many
+ */
+class FormHasManyTest extends TestCase
+{
+    /**
+     * 测试新增页面.
+     *
+     * @throws \Throwable
+     */
+    public function testCreatePage()
+    {
+        $this->browse(function (Browser $browser) {
+            $browser->visit(new PainterCreatePage());
+        });
+    }
+
+    /**
+     * 测试新增记录.
+     *
+     * @throws \Throwable
+     */
+    public function testAddNewRecord()
+    {
+        $this->browse(function (Browser $browser) {
+            $data = $this->data();
+
+            $browser
+                ->visit(new PainterCreatePage())
+                ->fill($data)
+                ->submit();
+
+            $paintings = $data['paintings'];
+            unset($data['paintings']);
+
+            $painter = Painter::query()->where($data)->first();
+
+            $this->assertTrue($painter instanceof Painter);
+            $this->assertEquals($painter->id, 1);
+
+            // painting表数据
+            $this->assertEquals(count($paintings), Painting::count());
+
+            foreach ($paintings as $painting) {
+                $painting['painter_id'] = $painter->id;
+
+                $this->seeInDatabase((new Painting())->getTable(), $painting);
+            }
+        });
+    }
+
+    /**
+     * 测试编辑页面
+     *
+     * @throws \Throwable
+     */
+    public function testEditPage()
+    {
+        $this->browse(function (Browser $browser) {
+            $data = $this->data();
+
+            $painter = $this->save($data);
+
+            $browser->visit(new PainterEditPage($painter));
+        });
+    }
+
+    /**
+     * @param array $data
+     *
+     * @return Painter
+     */
+    protected function save($data)
+    {
+        $painter = new Painter();
+        $painter->fill($data)->save();
+
+        foreach ($data['paintings'] as $painting) {
+            $painting['painter_id'] = $painter->getKey();
+
+            $painter->paintings()->insert($painting);
+        }
+
+        return $painter;
+    }
+
+    /**
+     * @return array
+     */
+    protected function data()
+    {
+        return [
+            'username' => 'uuu',
+            'bio'      => 'bxbxbxbxbxbx',
+            'paintings' => [
+                [
+                    'title' => '蒙娜丽莎',
+                    'body' => '(* ̄︶ ̄)',
+                    'completed_at' => now(),
+                ],
+                [
+                    'title' => '鸡蛋',
+                    'body' => '(* ̄︶ ̄)',
+                    'completed_at' => now(),
+                ],
+            ],
+        ];
+    }
+}

+ 1 - 1
tests/Browser/IndexTest.php → tests/Browser/Cases/IndexTest.php

@@ -1,6 +1,6 @@
 <?php
 
-namespace Tests\Browser;
+namespace Tests\Browser\Cases;
 
 use Laravel\Dusk\Browser;
 use Tests\TestCase;

+ 5 - 5
tests/Browser/MenuTest.php → tests/Browser/Cases/MenuTest.php

@@ -1,6 +1,6 @@
 <?php
 
-namespace Tests\Browser;
+namespace Tests\Browser\Cases;
 
 use Dcat\Admin\Models\Menu;
 use Laravel\Dusk\Browser;
@@ -108,16 +108,16 @@ class MenuTest extends TestCase
                     $browser->whenElementAvailable(new MenuEditForm($id), function (Browser $browser) use ($updates) {
                         // 检测表单
                         $browser->fill($updates);
-                    }, 3)
+                    }, 2)
                         ->assertSeeText(__('admin.edit'))
                         ->click('div')
                         ->whenElementAvailable(new MultipleSelect2('select[name="roles[]"]'), function (Browser $browser) {
                             $browser->choose(1);
                         }, 2)
                         ->clickLink(__('admin.submit'));
-                }, 3)
-                ->waitForText(__('admin.update_succeeded'), 3)
-                ->waitForLocation(test_admin_path('auth/menu'), 2)
+                }, 2)
+                ->waitForText(__('admin.update_succeeded'), 2)
+                ->waitForLocation(test_admin_path('auth/menu'), 1)
                 ->waitForText('balabala', 2);
 
             // 检测是否写入数据库

+ 1 - 1
tests/Browser/OperationLogTest.php → tests/Browser/Cases/OperationLogTest.php

@@ -1,6 +1,6 @@
 <?php
 
-namespace Tests\Browser;
+namespace Tests\Browser\Cases;
 
 use Dcat\Admin\Models\OperationLog;
 use Laravel\Dusk\Browser;

+ 22 - 2
tests/Browser/Components/Form/Field/HasMany.php

@@ -3,8 +3,8 @@
 namespace Tests\Browser\Components\Form\Field;
 
 use Laravel\Dusk\Browser;
-use PHPUnit\Framework\Assert as PHPUnit;
 use Tests\Browser\Components\Component;
+use Tests\PHPUnit;
 
 class HasMany extends Component
 {
@@ -124,7 +124,7 @@ JS
         $browser->assertVisible($groupSelector);
         $browser->assertVisible("{$groupSelector} {$this->formatSelectorWithoutPrefix($browser, '@remove')}");
 
-        return $callback ? $browser->with($groupSelector, $callback) : $browser;
+        return $callback ? $browser->extend($groupSelector, $callback) : $browser;
     }
 
     /**
@@ -170,4 +170,24 @@ JS
     {
         return $this->remove($browser, $this->getLastFormGroupIndex($browser));
     }
+
+    /**
+     * 获取hasMany内表单字段值.
+     *
+     * @param Browser $browser
+     * @param string $field
+     * @param string $value
+     *
+     * @return string|null
+     */
+    public function assertFormGroupInputValue(Browser $browser, $field, $value)
+    {
+        $input = $browser->script(
+                <<<JS
+return $('{$browser->resolver->format('.'.$field)}').val();
+JS
+        )[0] ?? null;
+
+        PHPUnit::assertEquals($input, $value);
+    }
 }

+ 2 - 1
tests/Browser/Components/Form/Field/Select2.php

@@ -32,7 +32,8 @@ class Select2 extends Component
      */
     public function assert(Browser $browser)
     {
-        $browser->assertHidden($this->selector())
+        $browser
+            ->assertVisible($this->selector())
             ->assertVisible('@container');
     }
 

+ 3 - 3
tests/Browser/Components/Form/MenuCreationForm.php

@@ -51,9 +51,9 @@ class MenuCreationForm extends Component
                     ->hasInput('icon')
                     ->hasInput('uri')
                     ->assertSelected('parent_id', 0)
-                    ->assert(new Tree('permissions'))
-                    ->assert(new Select2('select[name="parent_id"]'))
-                    ->assert(new MultipleSelect2('select[name="roles[]"]'));
+                    ->is(new Tree('permissions'))
+                    ->is(new Select2('select[name="parent_id"]'))
+                    ->is(new MultipleSelect2('select[name="roles[]"]'));
             });
     }
 

+ 3 - 3
tests/Browser/Components/Form/MenuEditForm.php

@@ -50,9 +50,9 @@ class MenuEditForm extends MenuCreationForm
                     ->hasInput('title')
                     ->hasInput('icon')
                     ->hasInput('uri')
-                    ->assert(new Tree('permissions'))
-                    ->assert(new Select2('select[name="parent_id"]'))
-                    ->assert(new MultipleSelect2('select[name="roles[]"]'));
+                    ->is(new Tree('permissions'))
+                    ->is(new Select2('select[name="parent_id"]'))
+                    ->is(new MultipleSelect2('select[name="roles[]"]'));
 
                 if (! $this->id) {
                     return;

+ 0 - 32
tests/Browser/HasManyTest.php

@@ -1,32 +0,0 @@
-<?php
-
-namespace Tests\Browser;
-
-use Laravel\Dusk\Browser;
-use Tests\Browser\Components\Form\Field\HasMany;
-use Tests\TestCase;
-
-/**
- * 一对多表单功能测试.
- *
- * @group has-many
- */
-class HasManyTest extends TestCase
-{
-    public function testCreate()
-    {
-        $this->browse(function (Browser $browser) {
-            $browser->visit(test_admin_path('tests/painters/create'))
-                ->assertPathIs(test_admin_path('tests/painters/create'))
-                ->with('form[method="POST"]', function (Browser $browser) {
-                    $browser->assertSeeText('Paintings')
-                        ->with(new HasMany('paintings'), function (Browser $browser) {
-                            // 点击新增
-                            $browser->add();
-                            // 点击删除
-                            $browser->removeLast();
-                        });
-                });
-        });
-    }
-}

+ 1 - 1
tests/Browser/Pages/MenuEditPage.php

@@ -38,7 +38,7 @@ class MenuEditPage extends Page
             ->assertSeeText(__('admin.delete'))
             ->assertSeeText(__('admin.submit'))
             ->assertSeeText(__('admin.reset'))
-            ->assert(new MenuEditForm($this->id));
+            ->is(new MenuEditForm($this->id));
     }
 
     /**

+ 111 - 0
tests/Browser/Pages/PainterCreatePage.php

@@ -0,0 +1,111 @@
+<?php
+
+namespace Tests\Browser\Pages;
+
+use Laravel\Dusk\Browser;
+use Tests\Browser\Components\Form\Field\HasMany;
+
+class PainterCreatePage extends Page
+{
+    /**
+     * Get the URL for the page.
+     *
+     * @return string
+     */
+    public function url()
+    {
+        return test_admin_path('tests/painters/create');
+    }
+
+    /**
+     * Assert that the browser is on the page.
+     *
+     * @param  Browser  $browser
+     * @return void
+     */
+    public function assert(Browser $browser)
+    {
+        $browser->assertPathIs($this->url())
+            ->with('@form', function (Browser $browser) {
+                $browser->assertSeeText('Paintings')
+                    ->with(new HasMany('paintings'), function (Browser $browser) {
+                        // 点击新增
+                        $browser->add();
+                        // 点击删除
+                        $browser->removeLast();
+                    });
+            });
+    }
+
+    /**
+     * Get the element shortcuts for the page.
+     *
+     * @return array
+     */
+    public function elements()
+    {
+        return [
+            '@form' => 'form[method="POST"]',
+        ];
+    }
+
+    /**
+     * 注入表单.
+     *
+     * @param Browser $browser
+     * @param array $input
+     *
+     * @return Browser
+     */
+    public function fill(Browser $browser, array $input)
+    {
+        return $browser->with('@form', function (Browser $browser) use ($input) {
+            $inputKeys = [
+                'username',
+                'bio',
+            ];
+
+            foreach ($input as $key => $value) {
+                if (in_array($key, $inputKeys, true)) {
+                    $browser->type($key, $value);
+
+                    continue;
+                }
+
+                if ($key === 'paintings') {
+                    $browser->within(new HasMany($key), function (Browser $browser) use ($value) {
+                        foreach ($value as $input) {
+                            $browser->add();
+
+                            $browser->withLastFormGroup(function (Browser $browser) use ($input) {
+                                foreach ($input as $k => $v) {
+                                    $browser->script(
+                                        <<<JS
+                                    $('{$browser->resolver->format('.'.$k)}').val('$v');
+JS
+                                    );
+                                }
+                            });
+                        }
+                    });
+                }
+            }
+        });
+    }
+
+    /**
+     * 提交表单.
+     *
+     * @param Browser $browser
+     *
+     * @return Browser
+     */
+    public function submit(Browser $browser)
+    {
+        return $browser->with('@form', function (Browser $browser) {
+            $browser->press(__('admin.submit'));
+            $browser->waitForTextInBody(__('admin.save_succeeded'), 2);
+            $browser->waitForLocation(test_admin_path('tests/painters'), 1);
+        });
+    }
+}

+ 54 - 0
tests/Browser/Pages/PainterEditPage.php

@@ -0,0 +1,54 @@
+<?php
+
+namespace Tests\Browser\Pages;
+
+use Laravel\Dusk\Browser;
+use Tests\Browser\Components\Form\Field\HasMany;
+use Tests\Models\Painter;
+use Tests\Models\Painting;
+use Tests\PHPUnit;
+
+class PainterEditPage extends PainterCreatePage
+{
+    /**
+     * @var \Tests\Models\Painter
+     */
+    protected $painter;
+
+    public function __construct($model)
+    {
+        $this->painter = $model instanceof Painter ? $model : Painter::findOrFail($model);
+
+        PHPUnit::assertTrue($this->painter->getKey() > 0);
+    }
+
+    /**
+     * Get the URL for the page.
+     *
+     * @return string
+     */
+    public function url()
+    {
+        return test_admin_path("tests/painters/{$this->painter->getKey()}/edit");
+    }
+
+    public function assert(Browser $browser)
+    {
+        parent::assert($browser);
+
+        $browser->with('@form', function (Browser $browser) {
+            $browser->assertInputValue('username', $this->painter->username);
+            $browser->assertInputValue('bio', $this->painter->bio);
+
+            $browser->within(new HasMany('paintings'), function (Browser $browser) {
+                $this->painter->paintings->each(function (Painting $painting, $key) use ($browser) {
+                    $browser->withFormGroup($key + 1, function (Browser $browser) use ($painting) {
+                        $browser->assertFormGroupInputValue('title', $painting->title);
+                        $browser->assertFormGroupInputValue('body', $painting->body);
+                        $browser->assertFormGroupInputValue('completed_at', $painting->completed_at);
+                    });
+                });
+            });
+        });
+    }
+}

+ 27 - 1
tests/BrowserExtension.php

@@ -2,7 +2,6 @@
 
 namespace Tests;
 
-use Facebook\WebDriver\Exception\TimeoutException;
 use Illuminate\Support\Arr;
 use Illuminate\Support\Str;
 use Laravel\Dusk\Browser;
@@ -98,6 +97,33 @@ JS
             'assertSeeText' => function (?string $text) {
                 return $this->assertSeeTextIn('', $text);
             },
+            // 判断全页面中是否存在文本
+            'assertSeeInBody' => function (?string $text) {
+                $resolver = clone $this->resolver;
+                $resolver->prefix = 'html';
+
+                $element = $resolver->findOrFail('');
+
+                PHPUnit::assertTrue(
+                    Str::contains(strtolower($element->getText()), strtolower($text)),
+                    "Did not see expected text [{$text}] within element [html]."
+                );
+
+                return $this;
+            },
+            // 等待全页面出现文本
+            'waitForTextInBody' => function ($text, $seconds = null) {
+                $text = Arr::wrap($text);
+
+                $message = $this->formatTimeOutMessage('Waited %s seconds for text', implode("', '", $text));
+
+                $resolver = clone $this->resolver;
+                $resolver->prefix = 'html';
+
+                return $this->waitUsing($seconds, 100, function () use ($resolver, $text) {
+                    return Str::contains($resolver->findOrFail('')->getText(), $text);
+                }, $message);
+            },
         ];
 
         foreach ($functions as $method => $callback) {

+ 1 - 2
tests/DuskTestCase.php

@@ -6,7 +6,6 @@ use Dcat\Admin\Models\Administrator;
 use Facebook\WebDriver\Chrome\ChromeOptions;
 use Facebook\WebDriver\Remote\DesiredCapabilities;
 use Facebook\WebDriver\Remote\RemoteWebDriver;
-use Laravel\Dusk\Browser;
 use Laravel\Dusk\TestCase as BaseTestCase;
 
 abstract class DuskTestCase extends BaseTestCase
@@ -62,7 +61,7 @@ abstract class DuskTestCase extends BaseTestCase
      */
     protected function newBrowser($driver)
     {
-        $browser = parent::newBrowser($driver)->resize(1566, 1080);
+        $browser = (new Browser($driver))->resize(1566, 1080);
 
         $browser->resolver->prefix = 'html';
 

+ 2 - 0
tests/Models/Painter.php

@@ -8,6 +8,8 @@ class Painter extends Model
 {
     protected $table = 'test_painters';
 
+    protected $fillable = ['username', 'bio',];
+
     public function paintings()
     {
         return $this->hasMany(Painting::class, 'painter_id');

+ 9 - 0
tests/PHPUnit.php

@@ -0,0 +1,9 @@
+<?php
+
+namespace Tests;
+
+use PHPUnit\Framework\Assert;
+
+class PHPUnit extends Assert
+{
+}

+ 3 - 0
tests/resources/.ide-helper.php

@@ -5,6 +5,7 @@ namespace Laravel\Dusk
     use Laravel\Dusk\Component;
 
     /**
+     * @method $this extend(string|Component $selector, \Closure $callback)
      * @method $this whenTextAvailable(string $text, $callbackOrSeconds = null, int $seconds = null)
      * @method $this whenElementAvailable($selector, $callbackOrSeconds = null, int $seconds = null)
      * @method $this hasInput($field)
@@ -12,6 +13,8 @@ namespace Laravel\Dusk
      * @method $this is(Component $component)
      * @method $this assertSeeTextIn(string $selector, string $text)
      * @method $this assertSeeText(string $text)
+     * @method $this assertSeeInBody(string $text)
+     * @method $this waitForTextInBody(string $text, int $seconds = null)
      */
     class Browser
     {