Our tests look good, as end to end integration test. - and it much better that our manually test strategy.

But they are out of date with respect to D8 core's test patterns, namely

1) The test extends WebTestBase when a variant of BrowserTestBase is expected .. there are core metas focusing solely on that migration... it is a big job which will only become relevant when simpletest is dropped starting D9!

2) The namespacing of the test follows and established but old pattern that not PSR compliant.
In short we should drop the class in namespace Drupal\simple_gmap\Tests - in favour of a class in Drupal\Tests\simple_gmap - that way we get a sub directory hanging from the root of the module .. outside of the 'src' directory which can, in the fullness of time, be dropped from production deployments of the module... [ A minor security golden rule is never leave test files on a publicly facing server ... it will only give hacker ideas! ]

If we follow cores direction, since we have AJAX on the Form under test, then our test becomes a function javascript test. This has some consequences

1) drupalPostForm() with array of fields as the second argument are prohibited/"need conversion"
2) drupalPostAjaxForm() need to be converted

In my opinion this adds complexity but removed some unwelcome code which is horribly hard coded to the implementation.

for example include_link in this snippet from the test

    $this->drupalPostAjaxForm(NULL, [], 'field_' . $field1 . '_settings_edit');
    $prefix = 'fields[field_' . $field1 . '][settings_edit_form][settings]';
    $this->drupalPostForm(NULL, [
      $prefix . '[include_map]' => TRUE,
      $prefix . '[include_static_map]' => TRUE,
      $prefix . '[apikey]' => $this->apiKey,
      $prefix . '[include_link]' => TRUE,
      $prefix . '[include_text]' => TRUE,
    ], 'Update');

The conversion looks for human readable labels without regard for the implementation and so in part looks like.

    $edit_form->checkField('include link to map'); 

Anyway I am 60% complete with the conversion ... I am just posting early to give a heads up ... detailing what is on my mind.

Comments

martin107 created an issue. See original summary.

jhodgdon’s picture

I believe that if you extend WebTestBase, the class needs to stay in the namespace where it is.

And the reason I did that rather than using the new classes is that the test needs to be run interactively and produce debug output, because the test is not completely automated -- you need to run the test and then look at the output to visually verify the module is working. You can't do that with the other classes.

So for now, this is a Won't Fix, as far as I can see.

jhodgdon’s picture

Just to clarify... I do realize that Simpletest is supposedly going away starting in Drupal 9. We'll have to go back to manually testing at that point unless Simpletest is ported to a contrib module, because the current semi-automated method will not work with the new classes. But until then it saves quite a bit of time to do it this way...

jhodgdon’s picture

If you're set on making tests that will work under Drupal 9, you could just add new classes, rather than removing the existing ones. Just a thought.

jhodgdon’s picture

Sorry, one more comment. I don't really think that the JavascriptTestBase class is viable. On the User Guide project, I started converting our WebTestBase test classes to JavascriptTestBase a few weeks ago, but I ran into so many bugs and difficulties, I gave it up (the code ended up as a bunch of terrible hacks). If I were you, I would wait until Core has been completely converted, because they will have to deal with the bugs in order to get their tests converted.

martin107’s picture

Status: Active » Needs review
StatusFileSize
new69.22 KB

I hope we can work together, in a community minded way, to bridge the clear differences in our positions
and produce good quality test that we are both happy with...

I take the line that the discussion is the most important thing and the code that follows is just the by-product.

Let me start by criticising myself -- my argument about hiding test on a production
server is poor if an attacker knows the module is open source!

Perhaps looking at some solid code will save 1000 words... I was mostly complete..
now I am 95% and I still think the idea has merit.

I am including a screen shot ( taken with a call to $this->createScreenshot() on the last page )

final screenshot

It shows the article page, with our 3 distinct maps on display - everything about to pass.

I believe strongly in posting early, post often .... so please bear in mind the test are broken

I gave it up (the code ended up as a bunch of terrible hacks).

Perhaps what I done will only confirm to your mind that it cannot be done cleanly.

I am involved in those core conversion issues and followed the discussion/development
of common code patterns closely...

I disagree that we should wait ... I will try and summarise recent goings-on and problems with
FunctionalJavascrtipt .. please jump in if you have an alternate perspective.

There WERE lots of unrepeatable tests which unfortunately passed enough to go through final committers review and that caused lots of reverts
- a major headache.

but the culprit was found, it was

$this->assertSession()->assertWaitOnAjaxRequest()

the method was not safe .. in any event the best working pattern has now been established.

"Wait for the page DOM to reach a final state that demonstrates the request/response cycle is complete before
continuing with assertions."

I am so used to following this pattern that converting this test was easy -
I would say "Thing slotted together nicely."

Returning to unresolved core FunctionalJavascript issues -- we do not have a good pattern of injecting
malicious data and asserting that that a http 500 error code is generated - that and the whole nighwatch thing but those issues do not factor here.

To aid review here is a crib sheet about deprecated functions.

1) $this->assertText() - mostly I used deliberately over-specified css selectors to provide additional assertions about what I was expecting
see calls to ->find('css', 'some tighly constrained css selector')

2) $this->assertLink() was replaced by $this->assertSession()->linkExists()

I appreciate reviewing things takes time ... so in the main I opted for a straight conversion where possible
here are some observations I have, which I did not change, but should go in a later issue/refactor

1) The test do some unnecesaary setup through the UI and doing things programatically will be faster.

Specifically it is good design to limit the UI manipulation to only the class under test
( our formatter and it settings form ) we have a few calls to 'admin/structure/types/manage/article/fields/add-field' at the top of the test which should be simplified.

2) The last few tests involve searching for an image with src that contains google.com/map we should be more precise and check all three images

3) I started this issue because I was thinking about writing test for #2896382: Support for Geolocation field. .. augmenting our test with an additional field seems to way to proceed there --- and that seems a natural fit for a FucntionalJavascript test.

I have tried speaking neutrally, laying out my argument ... I hope this provides a good framework for constructive criticism. Please feel free to tear anything apart.

Also If I can answer any questions in general about FunctionalJavascript testing please let me know.

I will continue to fix the final few lines of the test.

martin107’s picture

StatusFileSize
new21.96 KB

sorry here is the patch

Status: Needs review » Needs work

The last submitted patch, 7: initial-2913307-6.patch, failed testing. View results

jhodgdon’s picture

I'm glad you've had better luck with JavascriptTestBase than I did when I tried to convert the User Guide screenshot "tests" (using Simpletest to make automated screenshots for the User Guide). I just kept running into more and more problems and bugs, and in the end, gave up because I couldn't make it work, especially for non-English languages (which is a requirement for the screenshot automation for the User Guide). I filed several bugs, patched some, but still just couldn't get it to work out.

Anyway, I have no objection if you want to move forward on this, especially since you're able to make a screenshot that you can look at to do the manual section of the testing. I had forgotten about that.

martin107’s picture

StatusFileSize
new13.51 KB
new35.46 KB

There is a difference between what I see locally and what testbot reports .. but before I debug that I think I want to short circuit the whole thing...

I am about to propose a major redesign of the tests

Overnight I thought more about my unease in relation to configuration setup through the UI of field settings..not directly related to the class under test.

When I pushed that notion to its extreme this is what I came up with

"Use a dedicated test module to define a new content type containing three pre-configured fields
with default settings and values which map to our fields map1, map2 and xss"

The test then can be downgraded to a function level test ( extending BrowserTestBase )

By loading the test module and creating a 'simple_gmap_stress_test' node we in effect jump immediately to the final page - where we can start making relevant assertions.

Again a code snippet might save a thousand word... so instead of throwing away the existing changes I have, in parallel, created an outline of the test-submoule and the new test. It is a skeleton level piece of code where I have yet to specify the final assertions

To speak positively about this idea...

Maintenance is easier if we need to reasons with test setup we can copy the test module into module/custom and "drush en simple_gmap_stress_test" and visualize what is going on in a browser.

To speak negatively....

There is a small amount of conditional logic in SimpleGMapFormatter::settingsSummary() for example

    if ($include_static_map) {
      $summary[] = $this->t('Static map: @width x @height, Scale: @static_scale', [
        '@width' => $this->getSetting('iframe_width'),
        '@height' => $this->getSetting('iframe_height'),
        '@static_scale' => $this->getSetting('static_scale'),
      ]);
    }

and if we wanted to test it we would have to go back to javascript tests.

Anyway posting early ... posting often it is not yet worth moving to need review.

jhodgdon’s picture

I think that *both* (a) unit tests that isolate the testing to one class and (b) functional tests that verify the UI is working are both useful. In this case, a functional test with a human being looking at the final output (with JavaScript enabled to make the screenshot) to verify that the maps are actually showing, in my opinion, is crucial to verifying (at least occasionally, and when making changes to the code) that the module is actually working.

So, by all means, make a unit test as you are describing. But I think it's also a good idea to have a functional test that goes through the UI, and verifies the UI is working, with assertText or equivalents on the admin screens, etc., and that produces a screenshot we can look at. They can definitely be two separate tests if that is what you prefer. The previous test combined them, and I agree that this is not Best Practice.

On another note... in a D7 issue in this project, someone pointed out that if you re-submit the settings screen and there are special characters in it, you could have problems -- there is a double-sanitizing going on. So the functional test for D8 should test this. Let's see... Here it is: #2902178-10: Add title attribute to iframe (7.x). I made that into another issue just now for 7.x #2913846: Repeated submit of settings form causes problems, and we should also have an 8.x issue (or port that one to 8.x when done?) to at least add a test that verifies whether or not it's a problem -- it will need to use the settings UI.

Which again is why I think we need to test the UI for settings as well as a UI test that just tests the formatter. Both are good. :)

martin107’s picture

Status: Needs work » Needs review
StatusFileSize
new32.4 KB
new25.66 KB

The test then can be downgraded to a function level test ( extending BrowserTestBase )

Ah, I see I am unexpectedly the initiator or false rumours

"functionAL level tests" --- FunctionalTests are a little less complex to setup compared to FunctionalJavascriptTests

I did not mean to start the unit test rumour.... -:)... Anyway .. I have yo-yo'ed back to FunctionalJavascript Testing

When I spoke negatively in #10 .. I was talking out loud ... feeling my way forward

This adjustment, is my way of addressing both our concerns.

testSettingsForm - stops any regression blunders with regard to the settings forms. The thought behind it is - "let add a blank field which is not not required and then as needed we can make assertions to confirm we have driven each form values away from the default."

testFormatterOutput - addresses my drive for clarity - get in -- test something specific --- then get out.
.. it is only 25 line long.

compared to my last iteration the test class line count is reduced by 40% and executes 15% faster....
numbers not worth having... only as indication of a cleaner expression the test strategy.

Again this is skeleton code... while I think out loud ...and get the broad outline correct.. I am reading some core documents describing how we can use ->createSceenshot() to create assets which remain in the test record ... so we can do more of the manual steps automatically ... as this issue closes I hope to open a new issue so we can fully automate the remaining tests.

I will not be able to finish this until Sunday.. I just to see if I have resolved the difference between what I see locally and what testbot reports.

Status: Needs review » Needs work

The last submitted patch, 12: rumours-2913307-12.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

jhodgdon’s picture

We do need to do this -- the WebTestBase class is going away in Drupal 9.

Also recently the automatic running of the test has come up with a new deprecation error:

exception: [User deprecated] Line 175 of core/lib/Drupal/Core/Entity/EntityDefinitionUpdateManager.php:
EntityDefinitionUpdateManagerInterface::applyUpdates() is deprecated in 8.7.0 and will be removed before Drupal 9.0.0. Use \Drupal\Core\Entity\EntityDefinitionUpdateManagerInterface::getChangeList() and execute each entity type and field storage update manually instead. See https://www.drupal.org/node/3034742.trigger_error('EntityDefinitionUpdateManagerInterface::applyUpdates() is deprecated in 8.7.0 and will be removed before Drupal 9.0.0. Use \Drupal\Core\Entity\EntityDefinitionUpdateManagerInterface::getChangeList() and execute each entity type and field storage update manually instead. See https://www.drupal.org/node/3034742.', 16384) (Line: 175)
Drupal\Core\Entity\EntityDefinitionUpdateManager->applyUpdates() (Line: 51)
Drupal\simple_gmap\Tests\SimpleGmapTest->setUp() (Line: 967)

It looks to me like we can just remove this line from the test to get rid of the error:

    \Drupal::service('entity.definition_update_manager')->applyUpdates();

(line 51 of the test class, before any patches)

The reason I think it is OK to get rid of it is that if you look at current Core tests that create content types, they do not have a similar line. So it is most likely not necessary. For that matter, they don't seem to have these lines either:

   drupal_static_reset();
    \Drupal::entityManager()->clearCachedDefinitions();
    \Drupal::service('router.builder')->rebuild();

They just create the content type and keep going.

Also, I have had good success lately with WebDriverTestBase for Javascript-based testing. The old JavascriptTestBase that existed when this issue was last worked on is defunct.

jhodgdon’s picture

Additional deprecation

Call to deprecated method entityManager() of class Drupal.

This is from #3042777: Drupal 9 Deprecated Code Report

martin107’s picture

Status: Needs work » Needs review
StatusFileSize
new4.44 KB
new32.15 KB

Found a little time to work on this

a) fixed some coding standard nits
b) test should be run with the --browser option for a visual check. I have removed the hardcoded screenshot call that I was using for debug.
c) Replace flag as a group identifier.
d) Removed stray dependencies .. in the entwined config definitions - so no more unmet dependency warnings.

test still do not pass .. and I have not addressed all the review comments above but ... progress.

Status: Needs review » Needs work

The last submitted patch, 16: 2913307-16.patch, failed testing. View results

martin107’s picture

Status: Needs work » Needs review
StatusFileSize
new627 bytes
new29.38 KB

Cherry picking the obvious flaw.

Status: Needs review » Needs work

The last submitted patch, 18: 2913307-18.patch, failed testing. View results

jhodgdon’s picture

Some work has also been done recently on #3088320: Make sure Simple Google Maps is fully Drupal 9 compatible but ... didn't do the namespace stuff. Did get rid of deprecated stuff.

martin107’s picture

Status: Needs work » Needs review
StatusFileSize
new27.85 KB
new4.64 KB

@jhodgdon thanks for nudging me on this .. I did drop the ball..

1) Regarding the simple_gmap_stress_test module and the content type it defines.

a) In response to a deprecation notice I have added a match_limit entry to the uid settings type

Here is the change record https://www.drupal.org/node/3024684

b) One of the reasons the XSS test was failing was that the include_text was set to false.

2) mink is particular about the CSS selectors it accepts ... so this solves the next error.

-    $blank_row = $fields_form->find('css', "#field-blank");
+    $blank_row = $fields_form->find('css', "tr#field-blank");

3) setting a default theme is now needed to suppress deprecation notices.

+  /**
+   * {@inheritdoc}
+   */
+  protected $defaultTheme = 'stark';

4) I deleted tests/src/Functional/StressTest.php as it was not really testing much...

5) From core I begged borrowed and stole the test for XSS from the menu router test

Drupal\Tests\system\Functional\Menu\MenuRouterTest::doTestHookMenuIntegration()

so

+$this->assertEscaped("<script>alert(\"hello\");</script>", ENT_QUOTES, 'UTF-8');

I am posting early .. but I wanted to show progress .. I think I need another night to sit on this and think about it.

martin107’s picture

This passes locally for me .. famous last words.

Out of paranoid I did a fresh site-install on my test environment. No joy.
I can't see anything immediately obvious in the build/test artefacts.

It maybe the weekend before I can look at this more closely.

jhodgdon’s picture

Those kinds of errors can happen in JavaScript tests due to timing. If you click something in a test that has an Ajax response (such as opening up a field settings form or saving it on Manage Display), you need to put in a method call to

$this->assertSession()->assertWaitOnAjaxRequest();

This will pause the code until the Ajax thing actually happens.

martin107’s picture

Thanks I agree, assertWaitOnAjaxRequest(); is a possible solution, and certainly points to what I think is the underlying problem.

In relation to this article

https://www.drupal.org/docs/8/phpunit/phpunit-javascript-test-writing-tu...

assertWaitOnAjaxRequest() is listed as a way forward... However I did alot of conversions of FunctionalJavascript tests in core .. and I can say I got pushback during review.

As I understand it assertWaitOnAjaxRequest() remains as the solution of last resort and solutions of the form

::waitForElement()
::waitForElementVisible()
::waitForButton()
::waitForLink()
::waitForField()
::waitForId()

are preferred.

Next I want also to talk about another aspect of the test design I am unhappy with

when I run

./vendor/bin/phpunit -c core --verbose modules/contrib/simple_gmap

where --verbose does the same thing as --debug

debug/verbose .. outputs the visited pages to a browser ... and I can see the tests servers have reached out to google and the tests pull in the relevant images as they run.

I think this is bad, specifically my bad ... and I am also looking for ways of mocking request for google as we don't inspect the received images anyways.

jhodgdon’s picture

Yeah, waitForElement and similar functions are another alternative, but I don't think it matters too much. The important thing is to get the tests to run consistently, whatever it takes.

Regarding mocking Google... maybe this will help:
https://phpunit.de/manual/3.6/en/test-doubles.html#test-doubles.stubbing...

martin107’s picture

StatusFileSize
new30.34 KB
new4.97 KB

I have converted the tests with this signature

/**
   * Inspect the formatter output.
   *
   * Find one good example and two troublesome senarios.
   *
   * Troublesome senarious :-
   *   A complex character set.
   *   A XSS attack.
   */

into a Kernel test ... Sorry for the flip flopping...

In conversational language the new tests algorithm :-

Pass troublesome simple_gmap fields through the pipeline which is the 'view builder' and 'renderer'
and make assertions about the computed text.

To speak up for this approach ... the test infrastructure no longer inadvertently reaches out to google with requests, and Kernel test run much faster that Functional tests ( about 2 seconds on my machine )

One aspect of this approach I am in two minds about :-

Developers new to this module may have to install the stress_test module, inspect node/1 before they get understanding of what is going on .. some '*FormatterTests.php' files in core build up a field under test in a more accessible fashion.

Anyway this is the next iteration.

jhodgdon’s picture

I wouldn't worry about developers having to install modules to understand tests, or look in a test module to see some default config that a test depends on. That is very common. I say, proceed!

martin107’s picture

StatusFileSize
new1.46 KB
new30.7 KB

Jhodgdon thanks for following along.

(a) Something I noticed while scanning the sister issue.

#3088320: Make sure Simple Google Maps is fully Drupal 9 compatible

I am modifying the info file to pass the installation compatibility test that come with Drupal9

(b) I have fixed up some coding standard issue I introduced.

(c) I think I have resolved the test-instability by inserting a interlock ..

we now wait for the form to appear before manipulating the html inputs

martin107’s picture

StatusFileSize
new30.56 KB
new1.67 KB

A) I am simplifying the test code without any expectation of solving the underlying problem.

Previously the code could be read as

1) Find the form element.
2) Within the form find the row associated with the 'blank' field.
3) Within the row find the button
4) Click the button.

I have simplified the code to be

1) Find the button
2) click the button.

B) The test failure associated with the Drupal9 run fails for different reasons. I have fixed that.

At the moment I guess the failure is timing related or at least I have insufficiently locked things down.
Just in case it is not that I am documenting some version numbers in my local test setup

php 7.3.12
phpunit 7.5.18

martin107’s picture

Assigned: martin107 » Unassigned
StatusFileSize
new515 bytes
new30.5 KB

Ok .. that is unexpected ... the solution to nebulous questions like "why does it work only on my machine" are seldom straightforward.

The best I have is "well the solution has less moving parts".

Maybe the page and the form arrive in chunks and waiting for the button is the most robust solution.

If my solution is not complete... it will cause problems down the line...

As a side note

The test originally put Effiel tower in the form and checked for the output text 'Effiel tower'.

I removed it because it was not adding anything to the coverage. I did not adjust a related comment ... I am doing so now.

I think this is ready now.

martin107’s picture

Status: Needs review » Reviewed & tested by the community

I looked this over tonight and I think this is good to go ...

It has been nearly a year since I have commited anything and my RSA keys are askew..

I while fix up that up in the morning over breakfast and then push

  • martin107 authored 0ab2124 on 8.x-1.x
    Issue #2913307 by martin107, jhodgdon: Prepare Tests for upgrade
    
martin107’s picture

Status: Reviewed & tested by the community » Fixed

jhodgdon thanks for your comments -- they shaped the direction of this fix.

Status: Fixed » Closed (fixed)

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