Behat/Mink — pull request #386 by bevanr (2013-10-08 23:22:03+00:00)
Title: Prefer exact matches over partial matches for the 'named' selector

This change is an alternative to #385. It takes things a step further and changes the behaviour of the `named` selector engine to look for exact matches, then partial matches only if no exact matches were found. It exposes the original `named` selector engine as the `partial` selector engine (`PartialSelector`).

This breaks backwards compatibility of scenarios that use mink extension on test subjects which contain a partial match followed by an exact match. It may be desirable to allow the feature to be disabled (or enabled?) from `FeatureContext`.

A method like `Driver::enableExactMatches($option)` that configures the feature to match 'partial' only, `exact` only or to `prefer` exact matches could achieve this. However I am not sure if it should be a method on the `Driver`, `Session` or some other class. Also, I do not think it is a very clean solution. Thoughts?

Another more minor breakage to backwards compatibility is that the selectors registered in `SelectorsHandler` are now `css`, `partial` and `exact`. They were `css` and `named`. The `named` selector engine is no longer a registered implementation of `SelectorInterface`. Instead it is a pseudo-selector, like the `xpath` pseudo selector engine.

This breaks code that assumes the `named` selector engine is an implementation of the `SelectorInterface` and uses API methods such as `SelectorsHandler::getSelector('named')` or `AbstractNamedSelector::registerNamedXpath()` (previously `NamedSelector::registerNamedXpath()`).

I think this is an acceptable breakage, since it is probably common that the reason to use `registerNamedXpath()` is to implement this feature (e.g. [#320](https://github.com/Behat/Mink/pull/320#issuecomment-17219780)). And also because such code is easily upgradable once this new feature is understood and taken into consideration.

The essential logic of looking for exact matches then partial matches is implemented in the `Element` class' `find()` method. I considered implementing it in the `findAll()` method but decided against it since `findAll()` would become unreliable in the case where a caller expects a list of more than N partial (and exact) matches but only gets less than N exact matches. And because this new feature is mostly only relevant when the caller wants _one_ element—the best matching one.

This changeset solves the fatal error encountered in #385: _Cannot instantiate abstract class Behat\Mink\Selector\NamedSelector in vendor/symfony/dependency-injection/Symfony/Component/DependencyInjection/ContainerBuilder.php on line 963_:

The `NamedSelector` class becomes `AbstractNamedSelector` and `PartialSelector` classes. `ExactSelector` is the new class for the `exact` selector. `ExactSelector` and `PartialSelector` have the same structure and inheritance: `extends AbstractNamedSelector`. `ExactNamedSelector` and `PartialNamedSelector` may be better names.

To use this with Mink Extension the following changes must be made to [core.xml](https://github.com/Behat/MinkExtension/blob/2.0/src/Behat/MinkExtension/services/core.xml) for Symfony's Dependency Injection to discover classes and instantiate objects correctly:

```diff
--- core.xml.orig	2013-10-09 09:48:38.000000000 +1300
+++ core.xml	2013-10-09 09:48:31.000000000 +1300
@@ -10,7 +10,8 @@
Behat\Mink\Session
Behat\Mink\Selector\SelectorsHandler
Behat\Mink\Selector\CssSelector
- Behat\Mink\Selector\NamedSelector
+ Behat\Mink\Selector\PartialSelector
+ Behat\Mink\Selector\ExactSelector

Behat\MinkExtension\Context\ClassGuesser\MinkContextClassGuesser
Behat\MinkExtension\Context\Initializer\MinkAwareInitializer
@@ -32,8 +33,11 @@

-
-
+
+
+
+
+

```

I have not tested running the tests. Does Travis do this?

There is no test coverage for this new feature yet.