CommentFileSizeAuthor
#45 interdiff-2862510-43-45.txt2.84 KBmartin107
#45 enabled-2862510-45.patch29.39 KBmartin107
#43 composer-2862510-43.patch25.87 KBmartin107
#41 interdiff-2862510-37-41.txt6.85 KBmartin107
#41 MultiForm-2862510-41.patch131.23 KBmartin107
#37 MultiForm-2862510-37.patch25.46 KBmartin107
#37 interdiff-2862510-34-37.txt2.99 KBmartin107
#34 wotNoFrameworkTest-2862510-34.patch18.48 KBmartin107
#34 interdiff-2862510-32-34.txt653 bytesmartin107
#32 wotNoDialogTest-2862510-32.patch19.11 KBmartin107
#32 interdiff-2862510-25-32.txt675 bytesmartin107
#25 interdiff-17-25.txt728 bytesmartin107
#25 cycle-2862510-25.patch19.77 KBmartin107
#17 brittle-2862510-17.patch19.72 KBmartin107
#17 interdiff-2862510-15-17.txt1.46 KBmartin107
#15 interdiff-2862510-6-15.txt15.31 KBmartin107
#15 do-not-review-2862510-15.patch18.07 KBmartin107
#6 AjaxFormCacheTest-2862510-6.patch8.72 KBmartin107
#6 interdiff-2862510-4-6.txt2.36 KBmartin107
#4 early-2862510-4.patch6.7 KBmartin107
Support from Acquia helps fund testing for Drupal Acquia logo

Comments

dawehner created an issue. See original summary.

martin107’s picture

Assigned: Unassigned » martin107
Issue summary: View changes

added test to work on in the issue summary... patch coming..

dawehner’s picture

Thanks a ton for filing this issue!

martin107’s picture

Status: Active » Needs review
FileSize
6.7 KB

First cut, expected to fail.

Just moved files and adjusted namespaces.

There are many calls to drupalPostAjaxForm() which are going to need adjustment.

Status: Needs review » Needs work

The last submitted patch, 4: early-2862510-4.patch, failed testing.

martin107’s picture

I am working on this in small steps, as it fits my pattern of spare time.

In this iteration, AjaxFormCacheTest.php now passes..

So I have a chain of thought that effects some system architecture level decisions, depending on how we proceed....so comments please.

referring to https://www.drupal.org/docs/8/phpunit/phpunit-javascript-testing-tutorial

As we move away from Drupal\simpletest\AssertContentTrait methods like assertOptionWithDrupalSelector() can be simply done with a few lines of code - using lines of code :-

$element = $page->find('css', 'css selector');
$this->assertNotEmpty($field);

assertOptionSelectedWithDrupalSelector() however has just enough complexity for us to create some reusable trait..... For the moment I am NOT going to do that
I just want to see how the rest of the issue plays out....

here is the replacement code - to a single call to $this->assertOptionSelectedWithDrupalSelector()

    // Confirm option1 has been selected.
    $page = $session->getPage();
    $opt1_selector = $page->find('xpath', '//select[@data-drupal-selector="edit-test1"]//option[@value="option1"]');
    $this->assertNotEmpty($opt1_selector);
    $this->assertTrue($opt1_selector->isSelected());
martin107’s picture

Status: Needs work » Needs review

Status: Needs review » Needs work

The last submitted patch, 6: AjaxFormCacheTest-2862510-6.patch, failed testing.

martin107’s picture

I would like to discuss what is the intent of the conversion.

At what level we should be testing - integration or unit testing?

There is a repeating pattern in CommandsTest:testAjaxCommands() which I will express in pseudo-code

    // Tests the 'add_css' command.
    $commands = $this->drupalPostAjaxForm($form_path, $edit, ['op' => t("AJAX 'add_css' command")]);
    $expected = new AddCssCommand('my/file.css');
    $this->assertCommand($commands, $expected->render(), "'add_css' AJAX command issued with correct data.");

1) Simulate the user updating of a SELECT html element, which triggers a AJAX response/response exchange.
2) Compute the expected output AJAX command.
3) Verify the response in (1) is the expected AJAX command.

Mink is very good at simulating users pointing and clicking in a browser and analysing how the page has changed.
BUT this test is not how the browser is updated, but what response drupal generates.

Should I modify tests of this pattern to drop inspection of the response and just assert how the browser should look after update?

If we want to confirm the response then units tests are a faster test.

I am in two minds at this moment.

martin107’s picture

Assigned: martin107 » Unassigned
Issue summary: View changes

Changing the issue summary to note that the conversion of AjaxFormPageCacheTest
is being discussed in another issue.

I think while I consider the arguments from #3 .. it would be a good thing for me to work on that

michielnugter’s picture

Issue tags: +phpunit initiative

Thanks a lot for the progress on the issue!

The JavascriptTestBase test should not check if the Ajax command is added in the correct way as the simpletest did but actually test the result of the action, we have to check the DOM.

As you demonstrated in another issue (#2809519: Convert AJAX part of \Drupal\system\Tests\Ajax\AjaxFormPageCacheTest to JavascriptTestBase) the assertion that an ajax command is created as expected can be a UnitTest.

So I think these assertions should be converted to both Unit tests and Javascript tests to cover it completely.

martin107’s picture

So I think these assertions should be converted to both Unit tests and Javascript tests to cover it completely.

Thanks for all the help, while I work all this out in my head, I am slowly getting clarity,

that is a good way to think about it.

Looking at CommandsTest -- I think that should be a side issue... just to keep this issue reviewable.

martin107’s picture

Assigned: Unassigned » martin107

If you follow the conversation here

https://www.drupal.org/node/2809519#comment-12054242

My attempts to convert AjaxFormCacheTests need to be rewritten without assertWaitOnAjaxRequest

michielnugter++

michielnugter’s picture

@martin2017

No problem! It's great to see someone else working on the JavascriptTestBase tests :)

I am going to expand the article in the documentation later on to have some more pointers on how to write these tests, you really need to get used to dealing with Javascript actions in a test..

In almost all cases the assertWaitOnAjaxRequest method is not required, it just takes a little more time to think about how you're going to test the end result :)

martin107’s picture

Status: Needs work » Postponed
FileSize
18.07 KB
15.31 KB

I am postponing this issue for a short time ... I hope

Over in

https://www.drupal.org/node/2809519#comment-12056056

we are discussing a pattern for avoiding the troublesome assertWaitOnAjaxRequest()

That has thrown a spanner in the works here as I was using the contentious pattern in the next iteration of the patch

waitForElement('css', "option:contains('red')");

-- what I have is full of mistakes and partially converted tests.... it is not worth reviewing BUT for one point.

I am using the waitForElement with :contains almost exclusively

So I am postponing on standardization of the way forward.

michielnugter’s picture

Status: Postponed » Needs work
martin107’s picture

Status: Needs work » Needs review
FileSize
1.46 KB
19.72 KB

I am still playing with best practise patterns for testing what we want...

I have worked on AjaxInGroupTest.php I am still not particularly happy with it.

I still want a new method waitUntilTheSequenceOfAJAXCommandsInTheAJAXResponseObjectHasCompleted_I_Pinky_Swear-Honest!() -- did I say I am bad at method names...otherwise lots of alterations here are going to "hand crafted"/brittle/"hard to maintain".

In this case the inserted HTML is identical to what is already on the page....except for the build_id

A second issue I can see .... FormValuesTest the code below is simulating an attacker posting invalid AJAX requests
That is a valid thing to do ... but I am going to have to establish a new pattern to do this...I think.

    foreach (['null', 'empty', 'nonexistent'] as $key) {
      $element_name = 'select_' . $key . '_callback';
      $edit = [
        $element_name => 'red',
      ];
      $commands = $this->drupalPostAjaxForm('ajax_forms_test_get_form', $edit, $element_name);
      $this->assertResponse(500);
    }

Anyway my time is limited until the weekend....

Status: Needs review » Needs work

The last submitted patch, 17: brittle-2862510-17.patch, failed testing.

michielnugter’s picture

In this case the inserted HTML is identical to what is already on the page....except for the build_id

There should always be something checkable after an ajax response. Either by a change in the database or a change in the DOM. In this case it's only the build_id, a bit curious but hey, it's something :) We can still test this without the ajax request wait.

1. Get the build_id before triggering the ajax request
2. Trigger the ajax request
3. Use waitFor() to wait untill the build_id has changed.
4. Assert the build_id has changed.

A second issue I can see .... FormValuesTest the code below is simulating an attacker posting invalid AJAX requests
That is a valid thing to do ... but I am going to have to establish a new pattern to do this...I think.

Well, in this case I doubt the test should be in a JavaScriptTestBase. You cannot (or should not be able to) trigger an invalid request using the Drupal interface alone. Validation the status codes is on track to be removed for JavascriptTestBase by the way, see #2827014: Throw an exception when testing status code or response headers in functional JavaScript tests.

This test should therefore remain in BrowserTestBase and the call to drupalPostAjaxForm must be converted to something BrowserTestBase-y.

martin107’s picture

I have been working on converting DialogTest it is not a straight conversion

so I am splitting it off.

I want to add some test - remove some redundant bits.

martin107’s picture

From #19

This test should therefore remain in BrowserTestBase and the call to drupalPostAjaxForm must be converted to something BrowserTestBase-y.

Here is what I can see ....

At first look, I don't think we are going to acheive this using only methods in the relatively new

Drupal\Tests\BrowserTestBase

it seems we are going to have to do something creative/non-standard

We can't use

BrowserTestBase::drupalPostForm()

as code passed through

BrowserTestBase::submitForm()

where there is an assertion that the field must be valid/exists

    // Edit the form values.
    foreach ($edit as $name => $value) {
      $field = $assert_session->fieldExists($name, $form);   /// <--- this assertion 

      // Provide support for the values '1' and '0' for checkboxes instead of
      // TRUE and FALSE.
      // @todo Get rid of supporting 1/0 by converting all tests cases using
      // this to boolean values.
      $field_type = $field->getAttribute('type');
      if ($field_type === 'checkbox') {
        $value = (bool) $value;
      }

      $field->setValue($value);
    }

At the moment it looks like the best way is to execute some javascript in a FunctionJavascriptTest
executing a $.ajax command where we have complete control of the request object...

I want to find a better way.....

michielnugter’s picture

Just to let you know, I've read and tried to fully understand your reply but haven't figured out a useful reply yet. I hope to be able to make some time this weekend to figure it out.

martin107’s picture

Another idea occurs to me

What we have in core at the moment..

1) sends requests into drupal
2) checks the output of the server

So no javascript tests in what would be the client.

When good tests pass, it serves as an integration test ... in the sense that all sub-tasks must be in the chain for the DOM to update properly

I am thinking maybe we can get the extra coverage provided by the troublesome tests by adding assertions to a form like unit test

Anyway I will have more time over the weekend.

michielnugter’s picture

It's an interesting problem and I have been trying various things but haven't found a working solution yet, time for today is up though.

At first look, I don't think we are going to acheive this using only methods in the relatively new

Drupal\Tests\BrowserTestBase

it seems we are going to have to do something creative/non-standard

It does seem like it for now, I can't see a solution using the available methods.

At the moment it looks like the best way is to execute some javascript in a FunctionJavascriptTest
executing a $.ajax command where we have complete control of the request object...

I'd love to see using JavaScript to control the test as a last resort for now.

Options I see:
- Skip browser testing for the invalid callbacks and cover these in KernelTests
- Figure out a good way to make the Ajaxy calls in BrowserTestBase.
-- A good starting point would be $this->getSession()->visit().

martin107’s picture

I'd love to see using JavaScript to control the test as a last resort for now.

On reflection using $.ajax is so wrong .... well I could make it worse, but I would have to find a way of fitting in a call to eval()!

so wrong I am sorry I suggested it ...

What I have now is just a little nudge to get the test passing a few more minor steps

FormValuesTest::testSimpleAjaxFormValue()

now that we are driving things through mink we need to adjust an existing bit of code.

red is the default option selected - selecting

"red->green->blue"

in a loop fails because red is already selected so the first AJAX request response cycle fails. going

"green->blue->red"

fixes an issue.

martin107’s picture

Status: Needs work » Needs review

The last submitted patch, 15: do-not-review-2862510-15.patch, failed testing.

Status: Needs review » Needs work

The last submitted patch, 25: cycle-2862510-25.patch, failed testing.

martin107’s picture

Assigned: martin107 » Unassigned
Status: Needs work » Postponed

After a little nudge from dawehner

https://www.drupal.org/node/2862508#comment-12096171

I would like to see how we can solve the thorny issue using nightwatch ...

I will try and solve the issue as part of

https://www.drupal.org/node/2869825

martin107’s picture

I spent a little little time tonight, familiarizing myself with this issue after a break...

There is a comment and reaction of mine I need to correct

In number #11 when michielnugter says

So I think these assertions should be converted to both Unit tests and Javascript tests to cover it completely.

The correct push-back is to say the unit test already exist see Drupal\Tests\Core\Ajax\AjaxCommandsTest

It maybe that some other parallel effort has converted things in the inbetween time.

Version: 8.4.x-dev » 8.5.x-dev

Drupal 8.4.0-alpha1 will be released the week of July 31, 2017, which means new developments and disruptive changes should now be targeted against the 8.5.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

martin107’s picture

Status: Postponed » Needs review
FileSize
675 bytes
19.11 KB

I want to start this up again.

DialogTest is being worked on separately in another issue.

So I am cutting those changes out of this patch

Status: Needs review » Needs work

The last submitted patch, 32: wotNoDialogTest-2862510-32.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

martin107’s picture

Issue summary: View changes
Status: Needs work » Needs review
Related issues: +#2902029: Convert Modernise FrameworkTest
FileSize
653 bytes
18.48 KB

DialogTest was moved out into its own issue because it was fiddly and too intricate to work on in a bulk conversion issue.

Using the same reasoning I am splitting off FrameworkTest

1) Updating the issue summary
2) Cutting out FrameworkTest from this issue.

Status: Needs review » Needs work

The last submitted patch, 34: wotNoFrameworkTest-2862510-34.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

martin107’s picture

Sorry if this looks like this issue is proceeding slowly

Before I work on ElementValidationTest further .....

Looking at the test results above ... it reports :-

RuntimeException: Unfinished AJAX requests while tearing down a test

This screw up can be seen when manually repeating the tests... ( I have listed the step to reproduce in the spin off issue )

Now that this Function Javascript is moving closer to what the manually testing represents ... it also gets into an infinite loop.

That is a bug not a conversion task ... so I have spun off a side issue so that we build up a meaningful audit trail keeping things separate.

martin107’s picture

In this update, I worked on getting MultiFormTest closer to sane.

instead of drupalPostAjaxForm() ... this patch simulates pushing of buttons.

But I have hit another brick wall -- and so am splitting off a side issue because I want to keep bugs separate from conversion tasks

As testing moved closer to what a human would press and click, when the functional tests enable the module as in

drush en form_test

both the test and humans get this error.

ReflectionException: Class Drupal\form_test\EventSubscriber\FormTestEventSubscriber does not exist
martin107’s picture

Status: Needs work » Needs review
martin107’s picture

I am now going to focus on the two bug reports spun off from this issue.

Status: Needs review » Needs work

The last submitted patch, 37: MultiForm-2862510-37.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

martin107’s picture

Status: Needs work » Needs review
FileSize
131.23 KB
6.85 KB

MultiFormTests is now passing locally.

Like DialogTest and FrameworkTest is questionable if it should be moved out into its own patch
..I will let reviewers decide.

PS

The conversion of FrameworkTest crept back into the patch ... that is why the error count jumped up ...sorry I have stripped it out again.

Status: Needs review » Needs work

The last submitted patch, 41: MultiForm-2862510-41.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

martin107’s picture

Status: Needs work » Needs review
FileSize
25.87 KB

I accidently included composer.json and composer.lock files

martin107--

Status: Needs review » Needs work

The last submitted patch, 43: composer-2862510-43.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

martin107’s picture

Regarding FormValuesTest.php - back in April (#17)

    foreach (['null', 'empty', 'nonexistent'] as $key) {
      $element_name = 'select_' . $key . '_callback';
      $edit = [
        $element_name => 'red',
      ];
      $commands = $this->drupalPostAjaxForm('ajax_forms_test_get_form', $edit, $element_name);
      $this->assertResponse(500);
    }

I was looking at ways to convert this code snippet... I missed the fact that the elements were included in the output DOIM and it would take only a minor change --- to get a elegant fix.

The fix is to supply more than red as a option ... then through clicking the UI the 500 errors can be triggered an observed.

Locally it fails as php errors are not handled correctly ... I am just posting to see how testbot deals with errors.

Status: Needs review » Needs work

The last submitted patch, 45: enabled-2862510-45.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

martin107’s picture

Just making notes for myself ...when I come back to work on this some more.

I am still thinking about a working solution for the following code snippet :-


foreach (['null', 'empty', 'nonexistent'] as $key) {
$element_name = 'select_' . $key . '_callback';
// Updating the field will trigger a AJAX request/response.
$session->getPage()->selectFieldOption($element_name, 'green');
// The select element is disabled as the AJAX request is issued.
$this->assertSession()->waitForElement('css', "select[name=\"$element_name\"]:disabled");
// The select element is enabled as the response is receieved.
$this->assertSession()->waitForElement('css', "select[name=\"$element_name\"]:enabled");
// The request will fail.
$this->assertSession()->statusCodeEquals(500);
}

A) is is not working at the moment!

BUT

B)

Given

#2827014: Throw an exception when testing status code or response headers in functional JavaScript tests

and this change record

https://www.drupal.org/node/2857562

calls to statusCodeEquals() are wrong.

Version: 8.5.x-dev » 8.6.x-dev

Drupal 8.5.0-alpha1 will be released the week of January 17, 2018, which means new developments and disruptive changes should now be targeted against the 8.6.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Anonymous’s picture

Issue summary: View changes

Great work here! Thank you, @martin107 for this!

But perhaps this type of tests is the most inconvenient for mass conversion. Therefore, according to the #2809161: Convert Javascript/AJAX testing to use JavascriptTestBase, it was decided to decouple each test into a separate issue. So, it would be nice to decople your patch by issues (I updated the IS with info about them)

Also I have a couple of minor suggestions and ideas for converting for some of the tests. But I do not want to mix them in one place (especially with my bad English).

martin107’s picture

decouple each test into a separate issue.

Ok, this issue has stalled a little bit... your suggestion is a good way of working things out.

Anonymous’s picture

Issue summary: View changes
Status: Needs work » Postponed

@martin107, thanks again! Let's postponed this issue until the situation becomes clearer with child issues.

Version: 8.6.x-dev » 8.7.x-dev

Drupal 8.6.0-alpha1 will be released the week of July 16, 2018, which means new developments and disruptive changes should now be targeted against the 8.7.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Lendude’s picture

Status: Postponed » Active

Yeah splitting this was a good idea, some of the child issue have already landed. Lets leave this as a meta issue?

Lendude’s picture

Status: Active » Fixed

All the child issues are done! Thanks everybody.

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.