<?php

declare(strict_types=1);

use Codeception\Exception\ElementNotFound;
use Codeception\Exception\MalformedLocatorException;
use Codeception\Lib\Framework;
use Codeception\Test\Unit;
use PHPUnit\Framework\AssertionFailedError;
use PHPUnit\Framework\ExpectationFailedException;

/**
 * Author: davert
 * Date: 13.01.12
 *
 * Class TestsForMink
 * Description:
 *
 */
abstract class TestsForWeb extends Unit
{
    /**
     * @var \Codeception\Module\UniversalFramework
     */
    protected Framework $module;

    public function testAmOnPage()
    {
        $this->module->amOnPage('/');
        $this->module->see('Welcome to test app!');

        $this->module->amOnPage('/info');
        $this->module->see('Information');
    }

    public function testCurrentUrl()
    {
        $this->module->amOnPage('/info');
        $this->module->seeCurrentUrlEquals('/info');
        $this->module->dontSeeInCurrentUrl('/user');
        $this->module->dontSeeCurrentUrlMatches('~user~');

        $this->module->amOnPage('/form/checkbox');
        $this->module->seeCurrentUrlEquals('/form/checkbox');
        $this->module->seeInCurrentUrl('form');
        $this->module->seeCurrentUrlMatches('~form/.*~');
        $this->module->dontSeeCurrentUrlEquals('/');
        $this->module->dontSeeCurrentUrlMatches('~form/a~');
        $this->module->dontSeeInCurrentUrl('user');
    }


    public function testSee()
    {
        $this->module->amOnPage('/');
        $this->module->see('Welcome to test app!');
        $this->module->see('A wise man said: "debug!"');
        $this->module->see('Welcome to test app!', 'h1');

        $this->module->see('Some text with formatting on separate lines');
        $this->module->see('Some text with formatting on separate lines', '#area4');
        $this->module->see('on separate lines', '#area4 .someclass');

        //ensure backwards compatibility, this assertion passed before this change
        $this->module->see("Test Link \n\n\n    Test");
        //Single quote HTML entities must be decoded
        $this->module->see("please don't provide us any personal information.");

        $this->module->amOnPage('/info');
        $this->module->see('valuable', 'p');
        $this->module->see('valuable', 'descendant-or-self::body/p');

        $this->module->dontSee('Welcome');
        $this->module->dontSee('valuable', 'h1');
        $this->module->dontSee('Welcome', 'h6');
    }

    public function testDontSeeFailsWhenMultilineTextMatches()
    {
        $this->shouldFail();
        $this->module->amOnPage('/');
        $this->module->dontSee('Some text with formatting on separate lines');
    }

    public function testDontSeeFailsWhenMultilineTextMatchesInSelector()
    {
        $this->shouldFail();
        $this->module->amOnPage('/');
        $this->module->dontSee('Some text with formatting on separate lines', '#area4');
    }

    /**
     * @Issue https://github.com/Codeception/Codeception/issues/3114
     */
    public function testSeeIsCaseInsensitiveForUnicodeText()
    {
        $this->module->amOnPage('/info');
        $this->module->see('ссылочка');
        $this->module->see('ссылочка', 'a');
    }

    public function testDontSeeIsCaseInsensitiveForUnicodeText()
    {
        $this->expectException(AssertionFailedError::class);
        $this->module->amOnPage('/info');
        $this->module->dontSee('ссылочка');
    }

    public function testSeeInSource()
    {
        $this->module->amOnPage('/');
        $this->module->seeInSource('<h1>Welcome to test app!</h1>');
        $this->module->seeInSource('A wise man said: "debug!"');
        $this->module->dontSeeInSource('John Cleese');
    }

    public function testSeeInCurrentUrl()
    {
        $this->module->amOnPage('/info');
        $this->module->seeInCurrentUrl('/info');
    }

    public function testSeeLink()
    {
        $this->module->amOnPage('/external_url');
        $this->module->seeLink('Next');
        $this->module->seeLink('Next', 'http://codeception.com/');
        // Without TLD and trailing slash
        $this->module->dontSeeLink('Next', 'http://codeception');
    }

    public function testDontSeeLink()
    {
        $this->module->amOnPage('/external_url');
        $this->module->dontSeeLink('Back');
        $this->module->dontSeeLink('Next', '/fsdfsdf/');
    }

    public function testSeeLinkFailsIfTextDoesNotMatch()
    {
        $this->expectException(AssertionFailedError::class);
        $this->expectExceptionMessage("No links containing text 'Codeception' were found in page /external_url");
        $this->module->amOnPage('/external_url');
        $this->module->seeLink('Codeception');
    }

    public function testSeeLinkFailsIfHrefDoesNotMatch()
    {
        $this->expectException(AssertionFailedError::class);
        $this->expectExceptionMessage("No links containing text 'Next' and URL '/fsdfsdf/' were found in page /external_url");
        $this->module->amOnPage('/external_url');
        $this->module->seeLink('Next', '/fsdfsdf/');
    }

    public function testSeeLinkFailsIfHrefDoesNotMatchExactly()
    {
        $this->expectException(AssertionFailedError::class);
        $this->expectExceptionMessage("No links containing text 'Next' and URL 'http://codeception' were found in page /external_url");
        $this->module->amOnPage('/external_url');
        $this->module->seeLink('Next', 'http://codeception');
    }

    public function testDontSeeLinkFailsIfTextMatches()
    {
        $this->expectException(AssertionFailedError::class);
        $this->expectExceptionMessage("Link containing text 'Next' was found in page /external_url");
        $this->module->amOnPage('/external_url');
        $this->module->dontSeeLink('Next');
    }

    public function testDontSeeLinkFailsIfTextAndUrlMatches()
    {
        $this->expectException(AssertionFailedError::class);
        $this->expectExceptionMessage("Link containing text 'Next' and URL 'http://codeception.com/' was found in page /external_url");
        $this->module->amOnPage('/external_url');
        $this->module->dontSeeLink('Next', 'http://codeception.com/');
    }

    public function testSeeLinkMatchesRelativeLink()
    {
        $this->module->amOnPage('/info');
        $this->module->seeLink('Sign in!', '/login');
        $this->module->dontSeeLink('Sign in!', '/log');
    }

    public function testDontSeeLinkMatchesRelativeLink()
    {
        $this->expectException(AssertionFailedError::class);
        $this->expectExceptionMessage("Link containing text 'Sign in!' and URL '/login' was found in page /info");
        $this->module->amOnPage('/info');
        $this->module->dontSeeLink('Sign in!', '/login');
    }

    public function testClick()
    {
        $this->module->amOnPage('/');
        $this->module->click('More info');
        $this->module->seeInCurrentUrl('/info');

        $this->module->amOnPage('/');
        $this->module->click('#link');
        $this->module->seeInCurrentUrl('/info');

        $this->module->amOnPage('/');
        $this->module->click("descendant-or-self::a[@id = 'link']");
        $this->module->seeInCurrentUrl('/info');
    }

    public function testClickByName()
    {
        $this->module->amOnPage('/form/button');
        $this->module->click("btn0");

        $form = data::get('form');
        $this->assertSame('val', $form['text']);
    }

    public function testClickByLinkTitle()
    {
        $this->module->amOnPage('/');
        $this->module->click("Link Title");
        $this->module->seeInCurrentUrl('/info');
    }

    public function testClickOnContext()
    {
        $this->module->amOnPage('/');
        $this->module->click('More info', 'p');
        $this->module->seeInCurrentUrl('/info');

        $this->module->amOnPage('/');
        $this->module->click('More info', 'body>p');
        $this->module->seeInCurrentUrl('/info');
    }

    public function testCheckboxByCss()
    {
        $this->module->amOnPage('/form/checkbox');
        $this->module->checkOption('#checkin');
        $this->module->click('Submit');

        $form = data::get('form');
        $this->assertSame('agree', $form['terms']);
    }

    public function testCheckboxByName()
    {
        $this->module->amOnPage('/form/checkbox');
        $this->module->checkOption('terms');
        $this->module->click('Submit');

        $form = data::get('form');
        $this->assertSame('agree', $form['terms']);
    }

    public function testCheckboxByLabel()
    {
        $this->module->amOnPage('/form/checkbox');
        $this->module->checkOption('I Agree');
        $this->module->click('Submit');

        $form = data::get('form');
        $this->assertSame('agree', $form['terms']);
    }

    /**
     * @group testCheckboxArray
     * @Issue https://github.com/Codeception/Codeception/pull/1145
     */
    public function testCheckboxArray()
    {
        $this->module->amOnPage('/form/checkbox_array');
        $this->module->checkOption('#id2');
        $this->module->click('Submit');

        $form = data::get('form');
        $this->assertSame('second', reset($form['field']));
    }

    public function testSelectByCss()
    {
        $this->module->amOnPage('/form/select');
        $this->module->selectOption('form select[name=age]', 'adult');
        $this->module->click('Submit');

        $form = data::get('form');
        $this->assertSame('adult', $form['age']);
    }

    public function testSelectByName()
    {
        $this->module->amOnPage('/form/select');
        $this->module->selectOption('age', 'adult');
        $this->module->click('Submit');

        $form = data::get('form');
        $this->assertSame('adult', $form['age']);
    }

    public function testSelectByLabel()
    {
        $this->module->amOnPage('/form/select');
        $this->module->selectOption('Select your age', 'dead');
        $this->module->click('Submit');

        $form = data::get('form');
        $this->assertSame('dead', $form['age']);
    }

    public function testSelectByLabelAndOptionText()
    {
        $this->module->amOnPage('/form/select');
        $this->module->selectOption('Select your age', '21-60');
        $this->module->click('Submit');

        $form = data::get('form');
        $this->assertSame('adult', $form['age']);
    }

    public function testSeeSelectedOption()
    {
        $this->module->amOnPage('/form/select');
        $this->module->seeOptionIsSelected('#age', '60-100');
        $this->module->dontSeeOptionIsSelected('#age', '100-210');
    }

    public function testSeeSelectedOptionForRadioButton()
    {
        $this->module->amOnPage('/form/example6');
        $this->module->seeOptionIsSelected('input[name=frequency]', 'hour');
        $this->module->dontSeeOptionIsSelected('input[name=frequency]', 'week');
    }

    /**
     * @Issue https://github.com/Codeception/Codeception/issues/2733
     */
    public function testSeeSelectedOptionReturnsFirstOptionIfNotSelected()
    {
        $this->module->amOnPage('/form/complex');
        $this->module->seeOptionIsSelected('#age', 'below 13');
        $this->module->click('Submit');

        $form = data::get('form');
        $this->assertSame('child', $form['age'], 'first option was not submitted');
    }

    /**
     * @group testSubmitSeveralSubmitsForm
     * @Issue https://github.com/Codeception/Codeception/issues/1183
     */
    public function testSubmitSeveralSubmitsForm()
    {
        $this->module->amOnPage('/form/example8');
        $this->module->click('form button[value="second"]');

        $form = data::get('form');
        $this->assertSame('second', $form['submit']);
    }

    /**
     * Additional test to make sure no off-by-one related problem.
     *
     * @group testSubmitSeveralSubmitsForm
     * @Issue https://github.com/Codeception/Codeception/issues/1183
     */
    public function testSubmitLotsOfSubmitsForm()
    {
        $this->module->amOnPage('/form/example11');
        $this->module->click('form button[value="fifth"]');

        $form = data::get('form');
        $this->assertSame('fifth', $form['submit']);
    }

    public function testSelectMultipleOptionsByText()
    {
        $this->module->amOnPage('/form/select_multiple');
        $this->module->selectOption('What do you like the most?', array('Play Video Games', 'Have Sex'));
        $this->module->click('Submit');

        $form = data::get('form');
        $this->assertSame(array('play', 'adult'), $form['like']);
    }

    public function testSelectMultipleOptionsByValue()
    {
        $this->module->amOnPage('/form/select_multiple');
        $this->module->selectOption('What do you like the most?', array('eat', 'adult'));
        $this->module->click('Submit');

        $form = data::get('form');
        $this->assertSame(array('eat', 'adult'), $form['like']);
    }

    public function testHidden()
    {
        $this->module->amOnPage('/form/hidden');
        $this->module->click('Submit');

        $form = data::get('form');
        $this->assertSame('kill_people', $form['action']);
    }

    public function testTextareaByCss()
    {
        $this->module->amOnPage('/form/textarea');
        $this->module->fillField('textarea', 'Nothing special');
        $this->module->click('Submit');

        $form = data::get('form');
        $this->assertSame('Nothing special', $form['description']);
    }

    public function testTextareaByLabel()
    {
        $this->module->amOnPage('/form/textarea');
        $this->module->fillField('Description', 'Nothing special');
        $this->module->click('Submit');

        $form = data::get('form');
        $this->assertSame('Nothing special', $form['description']);
    }

    public function testTextFieldByCss()
    {
        $this->module->amOnPage('/form/field');
        $this->module->fillField('#name', 'Nothing special');
        $this->module->click('Submit');

        $form = data::get('form');
        $this->assertSame('Nothing special', $form['name']);
    }

    public function testTextFieldByName()
    {
        $this->module->amOnPage('/form/example1');
        $this->module->fillField('LoginForm[username]', 'davert');
        $this->module->fillField('LoginForm[password]', '123456');
        $this->module->click('Login');

        $login = data::get('form');
        $this->assertSame('davert', $login['LoginForm']['username']);
        $this->assertSame('123456', $login['LoginForm']['password']);
    }

    public function testTextFieldByLabel()
    {
        $this->module->amOnPage('/form/field');
        $this->module->fillField('Name', 'Nothing special');
        $this->module->click('Submit');

        $form = data::get('form');
        $this->assertSame('Nothing special', $form['name']);
    }

    public function testTextFieldByLabelWithoutFor()
    {
        $this->module->amOnPage('/form/field');
        $this->module->fillField('Other label', 'Nothing special');
        $this->module->click('Submit');

        $form = data::get('form');
        $this->assertSame('Nothing special', $form['othername']);
    }

    public function testFileFieldByCss()
    {
        $this->module->amOnPage('/form/file');
        $this->module->attachFile('#avatar', 'app/avatar.jpg');
        $this->module->click('Submit');
        $this->assertNotEmpty(data::get('files'));
        $files = data::get('files');
        $this->assertArrayHasKey('avatar', $files);
    }

    public function testFileFieldByLabel()
    {
        $this->module->amOnPage('/form/file');
        $this->module->attachFile('Avatar', 'app/avatar.jpg');
        $this->module->click('Submit');
        $this->assertNotEmpty(data::get('files'));
    }

    public function checkBoxes(): iterable {
        yield ['/form/checkbox_checked', ['css' => "#checkedbox1"], true];
        yield ['/form/checkbox_checked', ['css' => "#checkedbox2"], true];
        yield ['/form/checkbox', '#checkin', false];
        yield ['/form/checkbox', ['css' => '#checkin'], false];
        yield ['/form/checkbox', 'I Agree', false];
        yield ['/info', 'input[type=checkbox]', true];
        yield ['/info', ['css' => 'input[type=checkbox]'], true];
        yield ['/info', 'Checked', true];
    }

    #[\Codeception\Attribute\DataProvider('checkBoxes')]
    public function testSeeCheckboxIsNotChecked(string $page, string|array $selector, bool $checked): void
    {
        $this->module->amOnPage($page);
        if ($checked) {
            $this->expectException(ExpectationFailedException::class);
        }
        $this->module->dontSeeCheckboxIsChecked($selector);
    }

    #[\Codeception\Attribute\DataProvider('checkBoxes')]
    public function testSeeCheckboxIsChecked(string $page, string|array $selector, bool $checked): void
    {
        $this->module->amOnPage($page);
        if (!$checked) {
            $this->expectException(ExpectationFailedException::class);
        }
        $this->module->seeCheckboxIsChecked($selector);
    }
    public function testSeeWithNonLatin()
    {
        $this->module->amOnPage('/info');
        $this->module->see('на');
    }

    public function testSeeWithNonLatinAndSelectors()
    {
        $this->module->amOnPage('/info');
        $this->module->see('Текст', 'p');
    }

    public function testSeeInFieldOnInput()
    {
        $this->module->amOnPage('/form/field');
        $this->module->seeInField('Name', 'OLD_VALUE');
        $this->module->seeInField('input[name=name]', 'OLD_VALUE');
        $this->module->seeInField('descendant-or-self::input[@id="name"]', 'OLD_VALUE');
    }

    public function testSeeInFieldForEmptyInput()
    {
        $this->module->amOnPage('/form/empty');
        $this->module->seeInField('#empty_input', '');
    }

    public function testSeeInFieldOnTextarea()
    {
        $this->module->amOnPage('/form/textarea');
        $this->module->seeInField('Description', 'sunrise');
        $this->module->seeInField('textarea', 'sunrise');
        $this->module->seeInField('descendant-or-self::textarea[@id="description"]', 'sunrise');
    }

    public function testSeeInFieldForEmptyTextarea()
    {
        $this->module->amOnPage('/form/empty');
        $this->module->seeInField('#empty_textarea', '');
    }

    public function testSeeInFieldOnCheckbox()
    {
        $this->module->amOnPage('/form/field_values');
        $this->module->dontSeeInField('checkbox[]', 'not seen one');
        $this->module->seeInField('checkbox[]', 'see test one');
        $this->module->dontSeeInField('checkbox[]', 'not seen two');
        $this->module->seeInField('checkbox[]', 'see test two');
        $this->module->dontSeeInField('checkbox[]', 'not seen three');
        $this->module->seeInField('checkbox[]', 'see test three');
    }

    public function testSeeInFieldWithBoolean()
    {
        $this->module->amOnPage('/form/field_values');
        $this->module->seeInField('checkbox1', true);
        $this->module->dontSeeInField('checkbox1', false);
        $this->module->seeInField('checkbox2', false);
        $this->module->dontSeeInField('checkbox2', true);
        $this->module->seeInField('radio2', true);
        $this->module->dontSeeInField('radio2', false);
        $this->module->seeInField('radio3', false);
        $this->module->dontSeeInField('radio3', true);
    }

    public function testSeeInFieldOnRadio()
    {
        $this->module->amOnPage('/form/field_values');
        $this->module->seeInField('radio1', 'see test one');
        $this->module->dontSeeInField('radio1', 'not seen one');
        $this->module->dontSeeInField('radio1', 'not seen two');
        $this->module->dontSeeInField('radio1', 'not seen three');
    }

    public function testSeeInFieldOnSelect()
    {
        $this->module->amOnPage('/form/field_values');
        $this->module->seeInField('select1', 'see test one');
        $this->module->seeInField('select1', 'Selected');
        $this->module->dontSeeInField('select1', 'not seen one');
        $this->module->dontSeeInField('select1', 'not seen two');
        $this->module->dontSeeInField('select1', 'not seen three');
        $this->module->dontSeeInField('select1', 'Not selected');
    }

    public function testSeeInFieldEmptyValueForUnselectedSelect()
    {
        $this->module->amOnPage('/form/field_values');
        $this->module->seeInField('select3', '');
    }

    public function testSeeInFieldOnSelectMultiple()
    {
        $this->module->amOnPage('/form/field_values');
        $this->module->dontSeeInField('select2', 'not seen one');
        $this->module->seeInField('select2', 'see test one');
        $this->module->dontSeeInField('select2', 'not seen two');
        $this->module->seeInField('select2', 'see test two');
        $this->module->dontSeeInField('select2', 'not seen three');
        $this->module->seeInField('select2', 'see test three');
    }

    public function testSeeInFieldWithExactMatch()
    {
        $this->module->amOnPage('/form/field_values');
        $this->module->seeInField(array('name' => 'select2'), 'see test one');
    }

    public function testDontSeeInFieldOnInput()
    {
        $this->module->amOnPage('/form/field');
        $this->module->dontSeeInField('Name', 'Davert');
        $this->module->dontSeeInField('input[name=name]', 'Davert');
        $this->module->dontSeeInField('descendant-or-self::input[@id="name"]', 'Davert');
    }

    public function testDontSeeInFieldOnTextarea()
    {
        $this->module->amOnPage('/form/textarea');
        $this->module->dontSeeInField('Description', 'sunset');
        $this->module->dontSeeInField('textarea', 'sunset');
        $this->module->dontSeeInField('descendant-or-self::textarea[@id="description"]', 'sunset');
    }

    public function testSeeInFormFields()
    {
        $this->module->amOnPage('/form/field_values');
        $params = [
            'checkbox[]' => [
                'see test one',
                'see test two',
            ],
            'radio1' => 'see test one',
            'checkbox1' => true,
            'checkbox2' => false,
            'select1' => 'see test one',
            'select2' => [
                'see test one',
                'see test two',
                'see test three'
            ]
        ];
        $this->module->seeInFormFields('form', $params);
    }

    public function testSeeInFormFieldsFails()
    {
        $this->module->amOnPage('/form/field_values');
        $this->expectException(AssertionFailedError::class);
        $params = [
            'radio1' => 'something I should not see',
            'checkbox1' => true,
            'checkbox2' => false,
            'select1' => 'see test one',
            'select2' => [
                'see test one',
                'see test two',
                'see test three'
            ]
        ];
        $this->module->seeInFormFields('form', $params);
    }

    public function testDontSeeInFormFields()
    {
        $this->module->amOnPage('/form/field_values');
        $params = [
            'checkbox[]' => [
                'not seen one',
                'not seen two',
            ],
            'radio1' => 'not seen one',
            'checkbox1' => false,
            'checkbox2' => true,
            'select1' => 'not seen one',
            'select2' => [
                'not seen one',
                'No where to be seen'
            ]
        ];
        $this->module->dontSeeInFormFields('form', $params);
    }

    public function testDontSeeInFormFieldsFails()
    {
        $this->module->amOnPage('/form/field_values');
        $this->expectException(AssertionFailedError::class);
        $params = [
            'checkbox[]' => [
                'wont see this anyway',
                'see test one',
            ],
            'select2' => [
                'not seen one',
                'No where to be seen'
            ]
        ];
        $this->module->dontSeeInFormFields('form', $params);
    }

    public function testSeeInFormFieldsWithAssociativeArrays()
    {
        $this->module->amOnPage('/form/example17');
        $this->module->seeInFormFields('form', [
            'FooBar' => ['bar' => 'baz'],
            'Food'   => ['beer' => ['yum' => ['yeah' => 'mmhm']]],
        ]);
    }

    public function testSeeInFieldWithNonLatin()
    {
        $this->module->amOnPage('/info');
        $this->module->seeInField('rus', 'Верно');
    }

    public function testApostrophesInText()
    {
        $this->module->amOnPage('/info');
        $this->module->see("Don't do that at home!");
        $this->module->see("Don't do that at home!", 'h3');
    }

    public function testSign()
    {
        $this->module->amOnPage('/info');
        $this->module->seeLink('Sign in!');
        $this->module->amOnPage('/info');
        $this->module->click('Sign in!');
    }

    public function testGrabTextFrom()
    {
        $this->module->amOnPage('/');
        $result = $this->module->grabTextFrom('h1');
        $this->assertSame("Welcome to test app!", $result);
        $result = $this->module->grabTextFrom('descendant-or-self::h1');
        $this->assertSame("Welcome to test app!", $result);
        $result = $this->module->grabTextFrom('~Welcome to (\w+) app!~');
        $this->assertSame('test', $result);
    }

    public function testGrabValueFrom()
    {
        $this->module->amOnPage('/form/hidden');
        $result = $this->module->grabValueFrom('#action');
        $this->assertSame("kill_people", $result);
        $result = $this->module->grabValueFrom("descendant-or-self::form/descendant::input[@name='action']");
        $this->assertSame("kill_people", $result);
        $this->module->amOnPage('/form/textarea');
        $result = $this->module->grabValueFrom('#description');
        $this->assertSame('sunrise', $result);
        $this->module->amOnPage('/form/select');
        $result = $this->module->grabValueFrom('#age');
        $this->assertSame('oldfag', $result);
    }

    /**
     * @see https://github.com/Codeception/Codeception/issues/3866
     */
    public function testGrabValueFromWithFillField()
    {
        $this->module->amOnPage('/form/bug3866');
        $this->module->fillField('empty', 'new value');

        $result = $this->module->grabValueFrom('#empty');
        $this->assertSame('new value', $result);
        $this->module->fillField('empty_textarea', 'new value');
        $result = $this->module->grabValueFrom('#empty_textarea');
        $this->assertSame('new value', $result);
        $this->module->fillField('//textarea[@name="textarea[name][]"]', 'new value');
        $result = $this->module->grabValueFrom('#textarea_with_square_bracket');
        $this->assertSame('new value', $result);
    }

    public function testGrabAttributeFrom()
    {
        $this->module->amOnPage('/search');
        $this->assertSame('get', $this->module->grabAttributeFrom('form', 'method'));
    }

    public function testLinksWithSimilarNames()
    {
        $this->module->amOnPage('/');
        $this->module->click('Test Link');
        $this->module->seeInCurrentUrl('/form/file');
        $this->module->amOnPage('/');
        $this->module->click('Test');
        $this->module->seeInCurrentUrl('/form/hidden');
    }

    public function testLinksWithDifferentContext()
    {
        $this->module->amOnPage('/');
        $this->module->click('Test', '#area1');
        $this->module->seeInCurrentUrl('/form/file');
        $this->module->amOnPage('/');
        $this->module->click('Test', '#area2');
        $this->module->seeInCurrentUrl('/form/hidden');
    }

    public function testSeeElementOnPage()
    {
        $this->module->amOnPage('/form/field');
        $this->module->seeElement('input[name=name]');
        $this->module->seeElement('input', ['name' => 'name']);
        $this->module->seeElement('input', ['id' => 'name']);
        $this->module->seeElement('descendant-or-self::input[@id="name"]');
        $this->module->dontSeeElement('#something-beyond');
        $this->module->dontSeeElement('input', ['id' => 'something-beyond']);
        $this->module->dontSeeElement('descendant-or-self::input[@id="something-beyond"]');
    }

    // regression test. https://github.com/Codeception/Codeception/issues/587
    public function testSeeElementOnPageFails()
    {
        $this->expectException(AssertionFailedError::class);
        $this->module->amOnPage('/form/field');
        $this->module->dontSeeElement('input[name=name]');
    }

    public function testCookies()
    {
        $cookie_name = 'test_cookie';
        $cookie_value = 'this is a test';
        $this->module->amOnPage('/');
        $this->module->setCookie('nocookie', '1111');
        $this->module->setCookie($cookie_name, $cookie_value);
        $this->module->setCookie('notthatcookie', '22222');


        $this->module->seeCookie($cookie_name);
        $this->module->dontSeeCookie('evil_cookie');

        $cookie = $this->module->grabCookie($cookie_name);
        $this->assertSame($cookie_value, $cookie);

        $this->module->resetCookie($cookie_name);
        $this->module->dontSeeCookie($cookie_name);
    }

    public function testCookiesWithPath()
    {
        $cookie_name = 'cookie';
        $cookie_value = 'tasty';
        $this->module->amOnPage('/info');
        $this->module->setCookie($cookie_name, $cookie_value, ['path' => '/info']);

        $this->module->seeCookie($cookie_name, ['path' => '/info']);
        $this->module->dontSeeCookie('evil_cookie');

        $cookie = $this->module->grabCookie($cookie_name, ['path' => '/info']);
        $this->assertSame($cookie_value, $cookie);

        $this->module->resetCookie($cookie_name, ['path' => '/info']);
        $this->module->dontSeeCookie($cookie_name, ['path' => '/info']);
        $this->module->dontSeeCookie($cookie_name);
    }

    public function testSendingCookies()
    {
        $this->module->amOnPage('/');
        $this->module->setCookie('nocookie', '1111');
        $this->module->amOnPage('/cookies');
        $this->module->see('nocookie', 'pre');
    }

    public function testPageTitle()
    {
        $this->module->amOnPage('/');
        $this->module->seeInTitle('TestEd Beta 2.0');
        $this->module->dontSeeInTitle('Welcome to test app');

        $this->module->amOnPage('/info');
        $this->module->dontSeeInTitle('TestEd Beta 2.0');
    }

    public function testSeeFails()
    {
        $this->shouldFail();
        $this->module->amOnPage('/');
        $this->module->see('Text not here');
    }

    public function testSeeInsideFails()
    {
        $this->shouldFail();
        $this->module->amOnPage('/info');
        $this->module->see('woups', 'p');
    }

    public function testDontSeeInInsideFails()
    {
        $this->shouldFail();
        $this->module->amOnPage('/info');
        $this->module->dontSee('interesting', 'p');
    }

    public function testSeeElementFails()
    {
        $this->shouldFail();
        $this->module->amOnPage('/info');
        $this->module->seeElement('.alert');
    }

    public function testDontSeeElementFails()
    {
        $this->shouldFail();
        $this->module->amOnPage('/info');
        $this->module->dontSeeElement('#back');
    }

    public function testSeeInFieldFail()
    {
        $this->shouldFail();
        $this->module->amOnPage('/form/empty');
        $this->module->seeInField('#empty_textarea', 'xxx');
    }

    public function testSeeInFieldOnTextareaFails()
    {
        $this->shouldFail();
        $this->module->amOnPage('/form/textarea');
        $this->module->dontSeeInField('Description', 'sunrise');
    }

    public function testSeeCheckboxIsNotCheckedFails()
    {
        $this->shouldFail();
        $this->module->amOnPage('/form/complex');
        $this->module->dontSeeCheckboxIsChecked('#checkin');
    }

    public function testSeeCheckboxCheckedFails()
    {
        $this->shouldFail();
        $this->module->amOnPage('/form/checkbox');
        $this->module->seeCheckboxIsChecked('#checkin');
    }

    public function testDontSeeElementOnPageFails()
    {
        $this->shouldFail();
        $this->module->amOnPage('/form/field');
        $this->module->dontSeeElement('descendant-or-self::input[@id="name"]');
    }

    public function testStrictLocators()
    {
        $this->module->amOnPage('/login');
        $this->module->seeElement(['id' => 'submit-label']);
        $this->module->seeElement(['name' => 'password']);
        $this->module->seeElement(['class' => 'optional']);
        $this->module->seeElement(['css' => 'form.global_form_box']);
        $this->module->seeElement(['xpath' => \Codeception\Util\Locator::tabIndex(4)]);
        $this->module->fillField(['name' => 'password'], '123456');
        $this->module->amOnPage('/form/select');
        $this->module->selectOption(['name' => 'age'], 'child');
        $this->module->amOnPage('/form/checkbox');
        $this->module->checkOption(['name' => 'terms']);
        $this->module->amOnPage('/');
        $this->module->seeElement(['link' => 'Test']);
        $this->module->click(['link' => 'Test']);
        $this->module->seeCurrentUrlEquals('/form/hidden');
    }

    public function testFailStrictLocators()
    {
        $this->shouldFail();
        $this->module->amOnPage('/form/checkbox');
        $this->module->checkOption(['name' => 'age']);
    }

    public function testExample1()
    {
        $this->module->amOnPage('/form/example1');
        $this->module->see('Login', 'button');
        $this->module->fillField('#LoginForm_username', 'davert');
        $this->module->fillField('#LoginForm_password', '123456');
        $this->module->checkOption('#LoginForm_rememberMe');
        $this->module->click('Login');

        $login = data::get('form');
        $this->assertSame('davert', $login['LoginForm']['username']);
        $this->assertSame('123456', $login['LoginForm']['password']);
        $this->assertNotEmpty($login['LoginForm']['rememberMe']);
    }

    public function testExample2()
    {
        $this->module->amOnPage('/form/example2');
        $this->module->fillField('input[name=username]', 'davert');
        $this->module->fillField('input[name=password]', '123456');
        $this->module->click('Log on');

        $login = data::get('form');
        $this->assertSame('davert', $login['username']);
        $this->assertSame('123456', $login['password']);
        $this->assertSame('login', $login['action']);
    }

    public function testAmpersand()
    {
        $this->module->amOnPage('/info');
        $this->module->see('Kill & Destroy');
        $this->module->see('Kill & Destroy', 'div');
    }

    /**
     * https://github.com/Codeception/Codeception/issues/1091
     */
    public function testExample4()
    {
        $this->module->amOnPage('/form/example4');
        $this->module->click(['css' => '#register button[type="submit"]']);

        $this->module->amOnPage('/form/example4');
        $this->module->click('#register button[type="submit"]');
    }

    /**
     * https://github.com/Codeception/Codeception/issues/1098
     */
    public function testExample5()
    {
        $this->module->amOnPage('/form/example5');
        $this->module->fillField('username', 'John');
        $this->module->fillField('password', '1234');
        $this->module->click('Login');
        $this->module->seeCurrentUrlEquals('/form/example5?username=John&password=1234');
    }

    public function testExample5WithSubmitForm()
    {
        $this->module->amOnPage('/form/example5');
        $this->module->submitForm('form', ['username' => 'John', 'password' => '1234']);
        $this->module->seeCurrentUrlEquals('/form/example5?username=John&password=1234');
    }

    public function testExample5WithParams()
    {
        $this->module->amOnPage('/form/example5?a=b');
        $this->module->fillField('username', 'John');
        $this->module->fillField('password', '1234');
        $this->module->click('Login');
        $this->module->seeCurrentUrlEquals('/form/example5?username=John&password=1234');
    }

    public function testExample5WithSubmitFormAndParams()
    {
        $this->module->amOnPage('/form/example5?a=b');
        $this->module->submitForm('form', ['username' => 'John', 'password' => '1234']);
        $this->module->seeCurrentUrlEquals('/form/example5?username=John&password=1234');
    }

    /**
     * @Issue https://github.com/Codeception/Codeception/issues/1212
     */
    public function testExample9()
    {
        $this->module->amOnPage('/form/example9');
        $this->module->attachFile('form[name=package_csv_form] input[name=xls_file]', 'app/avatar.jpg');
        $this->module->click('Upload packages', 'form[name=package_csv_form]');
        $this->assertNotEmpty(data::get('files'));
        $files = data::get('files');
        $this->assertArrayHasKey('xls_file', $files);
        $form = data::get('form');
        codecept_debug($form);
        $this->assertArrayHasKey('submit', $form);
        $this->assertArrayHasKey('MAX_FILE_SIZE', $form);
        $this->assertArrayHasKey('form_name', $form);
    }

    public function testSubmitForm()
    {
        $this->module->amOnPage('/form/complex');
        $this->module->submitForm('form', array(
                'name' => 'Davert',
                'description' => 'Is Codeception maintainer'
        ));
        $form = data::get('form');
        $this->assertSame('Davert', $form['name']);
        $this->assertSame('Is Codeception maintainer', $form['description']);
        $this->assertArrayNotHasKey('disabled_fieldset', $form);
        $this->assertArrayNotHasKey('disabled_fieldset_textarea', $form);
        $this->assertArrayNotHasKey('disabled_fieldset_select', $form);
        $this->assertArrayNotHasKey('disabled_field', $form);
        $this->assertSame('kill_all', $form['action']);
    }

    public function testSubmitFormWithFillField()
    {
        $this->module->amOnPage('/form/complex');
        $this->module->fillField('name', 'Kilgore Trout');
        $this->module->fillField('description', 'Is a fish');
        $this->module->submitForm('form', [
            'description' => 'Is from Iliyum, NY'
        ]);
        $form = data::get('form');
        $this->assertSame('Kilgore Trout', $form['name']);
        $this->assertSame('Is from Iliyum, NY', $form['description']);
    }

    public function testSubmitFormWithoutButton()
    {
        $this->module->amOnPage('/form/empty');
        $this->module->submitForm('form', array(
                'text' => 'Hello!'
        ));
        $form = data::get('form');
        $this->assertSame('Hello!', $form['text']);
    }

    public function testSubmitFormWithAmpersand()
    {
        $this->module->amOnPage('/form/submitform_ampersands');
        $this->module->submitForm('form', []);

        $form = data::get('form');
        $this->assertSame('this & that', $form['test']);
    }

    public function testSubmitFormWithArrayField()
    {
        $this->module->amOnPage('/form/example17');
        $this->module->submitForm('form', []);

        $data = data::get('form');
        $this->assertSame('baz', $data['FooBar']['bar']);
        $this->assertArrayNotHasKey('FooBar[bar]', $data);
    }

    public function testSubmitFormMultiSelectWithArrayParameter()
    {
        $this->module->amOnPage('/form/submitform_multiple');
        $this->module->submitForm('form', [
            'select' => [
                'see test one',
                'not seen four'
            ]
        ]);
        $form = data::get('form');
        $this->assertCount(2, $form['select']);
        $this->assertSame('see test one', $form['select'][0]);
        $this->assertSame('not seen four', $form['select'][1]);
    }

    public function testSubmitFormWithMultiSelect()
    {
        $this->module->amOnPage('/form/submitform_multiple');
        $this->module->submitForm('form', []);

        $form = data::get('form');
        $this->assertCount(2, $form['select']);
        $this->assertSame('see test one', $form['select'][0]);
        $this->assertSame('see test two', $form['select'][1]);
    }

    public function testSubmitFormCheckboxWithArrayParameter()
    {
        $this->module->amOnPage('/form/field_values');
        $this->module->submitForm('form', [
            'checkbox' => [
                'not seen one',
                'see test two',
                'not seen three'
            ]
        ]);
        $form = data::get('form');
        $this->assertCount(3, $form['checkbox']);
        $this->assertSame('not seen one', $form['checkbox'][0]);
        $this->assertSame('see test two', $form['checkbox'][1]);
        $this->assertSame('not seen three', $form['checkbox'][2]);
    }

    public function testSubmitFormCheckboxWithBooleanArrayParameter()
    {
        $this->module->amOnPage('/form/field_values');
        $this->module->submitForm('form', [
            'checkbox' => [
                true,
                false,
                true
            ]
        ]);
        $form = data::get('form');
        $this->assertCount(2, $form['checkbox']);
        $this->assertSame('not seen one', $form['checkbox'][0]);
        $this->assertSame('not seen two', $form['checkbox'][1]);
    }

    /**
     * https://github.com/Codeception/Codeception/issues/1381
     */
    public function testFillingFormFieldWithoutSubmitButton()
    {
        $this->module->amOnPage('/form/empty_fill');
        $this->module->fillField('test', 'value');
    }

    public function testSubmitFormWithDefaultTextareaValue()
    {
        $this->module->amOnPage('/form/textarea');
        $this->module->submitForm('form', []);

        $form = data::get('form');
        $this->assertSame('sunrise', $form['description']);
    }

    /**
     * @issue #1180
     */
    public function testClickLinkWithInnerSpan()
    {
        $this->module->amOnPage('/form/example7');
        $this->module->click("Buy Chocolate Bar");
        $this->module->seeCurrentUrlEquals('/');
    }

    /*
     * @issue #1304
     */
    public function testSelectTwoSubmitsByText()
    {
        $this->module->amOnPage('/form/select_two_submits');
        $this->module->selectOption('What kind of sandwich would you like?', '2');
        $this->module->click('Save');

        $form = data::get('form');
        $this->assertSame('2', $form['sandwich_select']);
    }

    public function testSelectTwoSubmitsByCSS()
    {
        $this->module->amOnPage('/form/select_two_submits');
        $this->module->selectOption("form select[name='sandwich_select']", '2');
        $this->module->click('Save');

        $form = data::get('form');
        $this->assertSame('2', $form['sandwich_select']);
    }

    protected function shouldFail()
    {
        $this->expectException(AssertionFailedError::class);
    }

    /**
     * https://github.com/Codeception/Codeception/issues/1051
     */
    public function testSubmitFormWithTwoSubmitButtonsSubmitsCorrectValue()
    {
        $this->module->amOnPage('/form/example10');
        $this->module->seeElement("#button2");
        $this->module->click("#button2");

        $form = data::get('form');
        $this->assertArrayHasKey('button2', $form);
        $this->assertArrayHasKey('username', $form);
        $this->assertSame('value2', $form['button2']);
        $this->assertSame('fred', $form['username']);
    }

    /**
     * https://github.com/Codeception/Codeception/issues/1051
     */
    public function testSubmitFormWithTwoSubmitButtonsSubmitsCorrectValueAfterFillField()
    {
        $this->module->amOnPage('/form/example10');
        $this->module->fillField("username", "bob");
        $this->module->click("#button2");

        $form = data::get('form');
        $this->assertArrayHasKey('button2', $form);
        $this->assertArrayHasKey('username', $form);
        $this->assertSame('value2', $form['button2']);
        $this->assertSame('bob', $form['username']);
    }

    /*
     * https://github.com/Codeception/Codeception/issues/1274
     */
    public function testSubmitFormWithDocRelativePathForAction()
    {
        $this->module->amOnPage('/form/example12');
        $this->module->submitForm('form', array(
            'test' => 'value'
        ));
        $this->module->seeCurrentUrlEquals('/form/example11');
    }

    public function testSubmitFormWithDocRelativePathForActionFromDefaultPage()
    {
        $this->module->amOnPage('/form/');
        $this->module->submitForm('form', array(
            'test' => 'value'
        ));
        $this->module->seeCurrentUrlEquals('/form/example11');
    }

    public function testLinkWithDocRelativeURLFromDefaultPage()
    {
        $this->module->amOnPage('/form/');
        $this->module->click('Doc-Relative Link');
        $this->module->seeCurrentUrlEquals('/form/example11');
    }

    /*
     * https://github.com/Codeception/Codeception/issues/1507
     */
    public function testSubmitFormWithDefaultRadioAndCheckboxValues()
    {
        $this->module->amOnPage('/form/example16');
        $this->module->submitForm('form', array(
            'test' => 'value'
        ));
        $form = data::get('form');
        $this->assertArrayHasKey('checkbox1', $form, 'Checkbox value not sent');
        $this->assertArrayHasKey('radio1', $form, 'Radio button value not sent');
        $this->assertSame('testing', $form['checkbox1']);
        $this->assertSame('to be sent', $form['radio1']);
    }

    public function testSubmitFormCheckboxWithBoolean()
    {
        $this->module->amOnPage('/form/example16');
        $this->module->submitForm('form', array(
            'checkbox1' => true
        ));
        $form = data::get('form');
        $this->assertArrayHasKey('checkbox1', $form, 'Checkbox value not sent');
        $this->assertSame('testing', $form['checkbox1']);

        $this->module->amOnPage('/form/example16');
        $this->module->submitForm('form', array(
            'checkbox1' => false
        ));
        $form = data::get('form');
        $this->assertArrayNotHasKey('checkbox1', $form, 'Checkbox value sent');
    }

    public function testSubmitFormWithCheckboxesWithoutValue()
    {
        $this->module->amOnPage('/form/checkbox_default_value');
        $this->module->submitForm('form', ['checkbox1' => true]);
        $this->assertSame('on', data::get('query')['checkbox1']);
    }

    public function testSubmitFormWithButtons()
    {
        $this->module->amOnPage('/form/form_with_buttons');
        $this->module->submitForm('form', array(
            'test' => 'value',
        ));
        $form = data::get('form');
        $this->assertFalse(
            isset($form['button1']) || isset($form['button2']) || isset($form['button3']) || isset($form['button4']),
            'Button values should not be set'
        );

        $this->module->amOnPage('/form/form_with_buttons');
        $this->module->submitForm('form', array(
            'test' => 'value',
        ), 'button3');
        $form = data::get('form');
        $this->assertFalse(
            isset($form['button1']) || isset($form['button2']) || isset($form['button4']),
            'Button values for buttons 1, 2 and 4 should not be set'
        );
        $this->assertArrayHasKey('button3', $form, 'Button value for button3 should be set');
        $this->assertSame($form['button3'], 'third', 'Button value for button3 should equal third');

        $this->module->amOnPage('/form/form_with_buttons');
        $this->module->submitForm('form', array(
            'test' => 'value',
        ), 'button4');
        $form = data::get('form');
        $this->assertFalse(
            isset($form['button1']) || isset($form['button2']) || isset($form['button3']),
            'Button values for buttons 1, 2 and 3 should not be set'
        );
        $this->assertArrayHasKey('button4', $form, 'Button value for button4 should be set');
        $this->assertSame($form['button4'], 'fourth', 'Button value for button4 should equal fourth');
    }

    /**
     * https://github.com/Codeception/Codeception/issues/1409
     */
    public function testWrongXpath()
    {
        $this->expectException(MalformedLocatorException::class);
        $this->module->amOnPage('/');
        $this->module->seeElement('//aas[asd}[sd]a[/[');
    }

    public function testWrongCSS()
    {
        $this->expectException(MalformedLocatorException::class);
        $this->module->amOnPage('/');
        $this->module->seeElement('.user#iasos<here');
    }

    public function testWrongStrictCSSLocator()
    {
        $this->expectException(MalformedLocatorException::class);
        $this->module->amOnPage('/');
        $this->module->seeElement(['css' => 'hel!1$<world']);
    }

    public function testWrongStrictXPathLocator()
    {
        $this->expectException(MalformedLocatorException::class);
        $this->module->amOnPage('/');
        $this->module->seeElement(['xpath' => 'hello<wo>rld']);
    }

    public function testFormWithFilesArray()
    {
        $this->module->amOnPage('/form/example13');
        $this->module->attachFile('foo[bar]', 'app/avatar.jpg');
        $this->module->attachFile('foo[baz]', 'app/avatar.jpg');
        $this->module->click('Submit');
        $this->assertNotEmpty(data::get('files'));
        $files = data::get('files');
        $this->assertArrayHasKey('bar', $files['foo']['name']);
        $this->assertArrayHasKey('baz', $files['foo']['name']);
    }

    public function testFormWithFileSpecialCharNames()
    {
        $this->module->amOnPage('/form/example14');
        $this->module->attachFile('foo bar', 'app/avatar.jpg');
        $this->module->attachFile('foo.baz', 'app/avatar.jpg');
        $this->module->click('Submit');
        $this->assertNotEmpty(data::get('files'));
        $files = data::get('files');
        $this->assertNotEmpty($files);
        $this->assertArrayHasKey('foo_bar', $files);
        $this->assertArrayHasKey('foo_baz', $files);
    }

    /**
     * @Issue https://github.com/Codeception/Codeception/issues/1454
     */
    public function testTextFieldByNameFirstNotCss()
    {
        $this->module->amOnPage('/form/example15');
        $this->module->fillField('title', 'Special Widget');
        $this->module->fillField('description', 'description');
        $this->module->fillField('price', '19.99');
        $this->module->click('Create');

        $data = data::get('form');
        $this->assertSame('Special Widget', $data['title']);
    }

    /**
     * @Issue https://github.com/Codeception/Codeception/issues/1535
     */
    public function testCheckingOptionsWithComplexNames()
    {
        $this->module->amOnPage('/form/bug1535');
        $this->module->checkOption('#bmessage-topicslinks input[value="4"]');
        $this->module->click('Submit');

        $data = data::get('form');
        $this->assertContains('4', $data['BMessage']['topicsLinks']);
    }

    /**
     * @Issue https://github.com/Codeception/Codeception/issues/1585
     * @Issue https://github.com/Codeception/Codeception/issues/1602
     */
    public function testUnreachableField()
    {
        $this->module->amOnPage('/form/bug1585');
        $this->module->fillField('textarea[name="captions[]"]', 'test2');
        $this->module->fillField('items[1][]', 'test3');
        $this->module->fillField('input[name="users[]"]', 'davert');
        $this->module->attachFile('input[name="files[]"]', 'app/avatar.jpg');
        $this->module->click('Submit');

        $data = data::get('form');
        $this->assertContains('test3', $data['items'][1]);
        $this->assertContains('test2', $data['captions']);
        $this->assertContains('davert', $data['users']);
    }

    public function testSubmitAdjacentForms()
    {
        $this->module->amOnPage('/form/submit_adjacentforms');
        $this->module->submitForm('#form-2', []);

        $data = data::get('form');
        $this->assertArrayHasKey('second-field', $data);
        $this->assertArrayNotHasKey('first-field', $data);
        $this->assertSame('Killgore Trout', $data['second-field']);
    }

    public function testSubmitAdjacentFormsByButton()
    {
        $this->module->amOnPage('/form/submit_adjacentforms');
        $this->module->fillField('first-field', 'First');
        $this->module->fillField('second-field', 'Second');
        $this->module->click('#submit1');

        $data = data::get('form');
        $this->assertArrayHasKey('first-field', $data);
        $this->assertArrayNotHasKey('second-field', $data);
        $this->assertSame('First', $data['first-field']);

        $this->module->amOnPage('/form/submit_adjacentforms');
        $this->module->fillField('first-field', 'First');
        $this->module->fillField('second-field', 'Second');
        $this->module->click('#submit2');

        $data = data::get('form');
        $this->assertArrayNotHasKey('first-field', $data);
        $this->assertArrayHasKey('second-field', $data);
        $this->assertSame('Second', $data['second-field']);
    }

    public function testArrayField()
    {
        $this->module->amOnPage('/form/example17');
        $this->module->seeInField('input[name="FooBar[bar]"]', 'baz');
        $this->module->seeInField('input[name="Food[beer][yum][yeah]"]', 'mmhm');
    }

    public function testFillFieldSquareBracketNames()
    {
        $this->module->amOnPage('/form/names-sq-brackets');
        $this->module->fillField('//input[@name="input_text"]', 'filling this input');
        $this->module->fillField('//input[@name="input[text][]"]', 'filling this input');

        $this->module->fillField('//textarea[@name="textarea_name"]', 'filling this textarea');
        $this->module->fillField('//textarea[@name="textarea[name][]"]', 'filling this textarea');
        $this->module->fillField('//textarea[@name="textarea[name][]"]', 'filling this textarea once again');

        $this->module->fillField('//textarea[@name="textarea_name"]', 'filling this textarea');
        $this->module->fillField('//textarea[@name="textarea[name][]"]', 'filling this textarea more');
        $this->module->fillField('//textarea[@name="textarea[name][]"]', 'filling this textarea most');
    }

    public function testSelectAndCheckOptionSquareBracketNames()
    {
        $this->module->amOnPage('/form/names-sq-brackets');
        $this->module->selectOption('//input[@name="input_radio_name"]', '1');
        $this->module->selectOption('//input[@name="input_radio_name"]', '2');

        $this->module->checkOption('//input[@name="input_checkbox_name"]');
        $this->module->checkOption('//input[@name="input_checkbox_name"]');

        $this->module->checkOption('//input[@name="input[checkbox][name][]"]');
        $this->module->checkOption('//input[@name="input[checkbox][name][]"]');
        $this->module->checkOption('//input[@name="input[checkbox][name][]"]');

        $this->module->selectOption('//select[@name="select_name"]', '1');

        $this->module->selectOption('//input[@name="input[radio][name][]"]', '1');
        $this->module->selectOption('//input[@name="input[radio][name][]"]', '2');
        $this->module->selectOption('//input[@name="input[radio][name][]"]', '1');

        $this->module->selectOption('//select[@name="select[name][]"]', '1');
    }

    public function testFillFieldWithAmpersand()
    {
        $this->module->amOnPage('/form/field');
        $this->module->fillField('Name', 'this & that');
        $this->module->click('Submit');

        $form = data::get('form');
        $this->assertSame('this & that', $form['name']);
    }

    public function testSeeInDeactivatedField()
    {
        $this->module->amOnPage('/form/complex');
        $this->module->seeInField('#disabled_field', 'disabled_field');
        $this->module->seeInField('#salutation', 'mr');
    }

    public function testSwitchToIframe()
    {
        $this->module->amOnPage('/iframe');
        $this->module->switchToIframe('content');
        $this->module->see('Is that interesting?');
        $this->module->click('Ссылочка');
    }

    public function testGrabMultiple()
    {
        $this->module->amOnPage('/info');

        $arr = $this->module->grabMultiple('#grab-multiple a:first-child');
        $this->assertCount(1, $arr);
        $this->assertSame('First', $arr[0]);

        $arr = $this->module->grabMultiple('#grab-multiple a');
        $this->assertCount(3, $arr);
        $this->assertSame('First', $arr[0]);
        $this->assertSame('Second', $arr[1]);
        $this->assertSame('Third', $arr[2]);

        // href for WebDriver with selenium returns a full link, so testing with ID
        $arr = $this->module->grabMultiple('#grab-multiple a', 'id');
        $this->assertCount(3, $arr);
        $this->assertSame('first-link', $arr[0]);
        $this->assertSame('second-link', $arr[1]);
        $this->assertSame('third-link', $arr[2]);
    }

    /**
     * @issue https://github.com/Codeception/Codeception/issues/2960
     */
    public function testClickMultiByteLink()
    {
        $this->module->amOnPage('/info');
        $this->module->click('Franšízy - pobočky');
        $this->module->seeCurrentUrlEquals('/');
    }

    /**
     * @issue https://github.com/Codeception/Codeception/issues/3528
     */
    public function testClickThrowsElementNotFoundExceptionWhenTextContainsNumber()
    {
        $this->expectException(ElementNotFound::class);
        $this->expectExceptionMessage("'Link 2' is invalid CSS and XPath selector and Link or Button element with 'name=Link 2' was not found.");
        $this->module->amOnPage('/info');
        $this->module->click('Link 2');
    }

    public function testClickExistingLinkWithTextContainingNumber()
    {
        $this->module->amOnPage('/info');
        $this->module->click('Link 3');
        $this->module->seeCurrentUrlEquals('/cookies');
    }

    public function testSelectOptionValueSelector()
    {
        $this->module->amOnPage('/form/select_selectors');
        $this->module->selectOption('age', ['value' => '20']);
        $this->module->click('Submit');

        $data = data::get('form');
        $this->assertSame('20', $data['age']);
    }

    public function testSelectOptionTextSelector()
    {
        $this->module->amOnPage('/form/select_selectors');

        $this->module->selectOption('age', ['text' => '20']);
        $this->module->seeOptionIsSelected('age', '20');

        $this->module->selectOption('age', ['text' => '21']);
        $this->module->seeOptionIsSelected('age', '21');
    }

    public function testClickButtonInLink()
    {
        $this->module->amOnPage('/form/button_in_link');
        $this->module->click('More Info');
        $this->module->seeCurrentUrlEquals('/info');
    }

    public function testClickButtonInLinkAndSpan()
    {
        $this->module->amOnPage('/form/button_in_link');
        $this->module->click('Span Info');
        $this->module->seeCurrentUrlEquals('/info');
    }

    public function testClickButtonInLinkUsingCssLocator()
    {
        $this->module->amOnPage('/form/button_in_link');
        $this->module->click(['css' => 'input[value="More Info"]']);
        $this->module->seeCurrentUrlEquals('/info');
    }

    public function testClickButtonInLinkAndSpanUsingCssLocator()
    {
        $this->module->amOnPage('/form/button_in_link');
        $this->module->click(['css' => 'input[value="Span Info"]']);
        $this->module->seeCurrentUrlEquals('/info');
    }

    public function testClickHashLink()
    {
        $this->module->amOnPage('/form/anchor');
        $this->module->click('Hash Link');
        $this->module->seeCurrentUrlEquals('/form/anchor');
    }

    public function testClickHashButton()
    {
        $this->module->amOnPage('/form/anchor');
        $this->module->click('Hash Button');
        $this->module->seeCurrentUrlEquals('/form/anchor');
    }

    public function testClickButtonWithForm()
    {
        $this->module->amOnPage('/form/button-not-in-form');
        $this->module->click('Submit 2');
        $this->module->seeCurrentUrlEquals('/form/button');
    }

    public function testClickButtonWithEmptyForm()
    {
        $this->module->amOnPage('/form/button-not-in-form');
        $this->expectException(\Codeception\Exception\TestRuntimeException::class);
        $this->module->click('Should not submit');
        $this->module->seeCurrentUrlEquals('/form/button-not-in-form');
    }

    public function testClickButtonNotInForm()
    {
        $this->module->amOnPage('/form/button-not-in-form');
        $this->expectException(\Codeception\Exception\TestRuntimeException::class);
        $this->module->click('Outside submit');
    }

    public function testClickButtonWithFormInvalidIdInside()
    {
        $this->module->amOnPage('/form/button-not-in-form');
        $this->expectException(\Codeception\Exception\TestRuntimeException::class);
        $this->module->click('Invalid form2');
    }

    public function testClickButtonWithFormInvalidIdOutside()
    {
        $this->module->amOnPage('/form/button-not-in-form');
        $this->expectException(\Codeception\Exception\TestRuntimeException::class);
        $this->module->click('Invalid form');
    }

    /**
     * https://github.com/Codeception/Codeception/issues/6022
     */
    public function testFillFieldNotInForm()
    {
        $this->module->amOnPage('/form/input-not-in-form');
        $this->module->seeElement("form",["id" => "form1"]);
        $this->module->seeElement("input",["name" => "username"]);
        $this->module->fillField("username","usr-code");
    }

    public function testSubmitHashForm()
    {
        $this->module->amOnPage('/form/anchor');
        $this->module->click('Hash Form');
        $this->module->seeCurrentUrlEquals('/form/anchor');
    }

    public function testClickingRelativeLinkHonoursBaseHref()
    {
        $this->module->amOnPage('/basehref');
        $this->module->click('Relative Link');
        $this->module->seeCurrentUrlEquals('/form/example7');
    }

    public function testSubmittingRelativeFormHonoursBaseHref()
    {
        $this->module->amOnPage('/basehref');
        $this->module->click('Relative Form');
        $this->module->seeCurrentUrlEquals('/form/example5');
    }

    public function testClickingRelativeLinkInContextHonoursBaseHref()
    {
        $this->module->amOnPage('/basehref');
        $this->module->click('Relative Link', 'p');
        $this->module->seeCurrentUrlEquals('/form/example7');
    }

    public function testSubmittingRelativeForminContextHonoursBaseHref()
    {
        $this->module->amOnPage('/basehref');
        $this->module->fillField('rus', 'test value');
        $this->module->click('Relative Form', '#button-container');
        $this->module->seeCurrentUrlEquals('/form/example5');
    }

    public function testClickingFormButtonInContextSubmitsOutOfContextFormElements()
    {
        $this->module->amOnPage('/basehref');
        $this->module->click('Relative Form', '#button-container');
        $this->assertArrayHasKey('rus', data::get('form'));
    }

    public function testAttachFileThrowsCorrectMessageWhenFileDoesNotExist()
    {
        $filename = 'does-not-exist.jpg';
        $expectedMessage = 'File does not exist: ' . codecept_data_dir($filename);
        $this->expectException(InvalidArgumentException::class);
        $this->expectExceptionMessage($expectedMessage);

        $this->module->amOnPage('/form/file');
        $this->module->attachFile('Avatar', $filename);
    }

    public function testPasswordArgument()
    {
        $this->module->amOnPage('/form/password_argument');
        $this->module->fillField('password', new \Codeception\Step\Argument\PasswordArgument('thisissecret'));
        $this->module->click('Submit');

        $data = data::get('form');
        $this->assertSame('thisissecret', $data['password']);
    }


    public function testCanResetHTTPAuthenticated()
    {
        $this->module->amHttpAuthenticated('user', 'pass');
        $this->module->amOnPage('/');

        $server = $this->module->client->getRequest()->getServer();
        $this->assertArrayHasKey('PHP_AUTH_USER', $server);
        $this->assertArrayHasKey('PHP_AUTH_PW', $server);
        $this->module->setServerParameters([]);
        $this->module->amOnPage('/');

        $server = $this->module->client->getRequest()->getServer();
        $this->assertArrayNotHasKey('PHP_AUTH_USER', $server);
        $this->assertArrayNotHasKey('PHP_AUTH_PW', $server);
    }

    public function testHaveServerParameter()
    {
        $this->module->haveServerParameter('my', 'param');
        $this->module->amOnPage('/');

        $server = $this->module->client->getRequest()->getServer();
        $this->assertArrayHasKey('my', $server);
    }

    public function testUncheckHidden()
    {
        $this->module->amOnPage('/form/uncheck_hidden');
        $this->module->uncheckOption('#coffee-id');
        $this->module->click("Submit Preference");

        $form = data::get('form');
        $this->assertSame('0', $form['coffee']);

        // test all other inputs are submitted as intended
        $this->assertSame('mouse', $form['wireless']);
        $this->assertSame('1', $form['tea']);
        $this->assertSame('on', $form['vanilla']); // 'on' is set internally
        $this->assertFalse(isset($form['butter']));

    }
}
