diff --git a/.ahoy.yml b/.ahoy.yml index 4aa7311..3f35781 100644 --- a/.ahoy.yml +++ b/.ahoy.yml @@ -125,6 +125,14 @@ commands: && ahoy cli "cd /app/build && ./vendor/bin/rector --clear-cache" \ && ahoy cli "cd /app/build && ./vendor/bin/phpcbf" + lint-docs: + usage: Lint docs. + cmd: ahoy cli "php docs.php --fail-on-change" + + update-docs: + usage: Lint docs. + cmd: ahoy cli "php docs.php" + test-bdd: usage: Run BDD tests. cmd: ahoy cli "cd /app/build && vendor/bin/behat -c /app/behat.yml --strict --colors $@" diff --git a/.circleci/config.yml b/.circleci/config.yml index 93d8b28..945b142 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -29,6 +29,10 @@ jobs: name: Lint code command: ahoy lint || [ "${CI_LINT_IGNORE_FAILURE:-0}" -eq 1 ] + - run: + name: Lint documentation + command: ahoy lint-docs || [ "${CI_LINT_DOCS_IGNORE_FAILURE:-0}" -eq 1 ] + - run: name: Run tests with coverage command: ahoy test-bdd-coverage || [ "${CI_TEST_IGNORE_FAILURE:-0}" -eq 1 ] diff --git a/MIGRATION.md b/MIGRATION.md index 2d5e932..ac69eb8 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -4,7 +4,7 @@ A migration map of the step definitions available in v2 to v3. | V2 | V3 | |------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| **[`DraggableViewsTrait`](src/DraggableViewsTrait.php) ([example](tests/behat/features/draggableviews.feature))** | | +| **[`DraggableviewsTrait`](src/DraggableviewsTrait.php) ([example](tests/behat/features/draggableviews.feature))** | | | `Then I save draggable views :view_id view :views_display_id display :bundle items in the following order:` | `When I save the draggable views items of the view :view_id and the display :views_display_id for the :bundle content in the following order:` | |   | | | **[`EckTrait`](src/EckTrait.php) ([example](tests/behat/features/eck.feature))** | | @@ -24,7 +24,7 @@ A migration map of the step definitions available in v2 to v3. | `Given I press the :char key` | `When I press the key :char` | | `Given I press the :char key on :selector` | `When I press the key :char on the element :selector` | |   | | -| **[`MetaTagTrait`](src/MetaTagTrait.php) ([example](tests/behat/features/metatag.feature))** | | +| **[`MetatagTrait`](src/MetatagTrait.php) ([example](tests/behat/features/metatag.feature))** | | | `Then I should see a meta tag with the following attributes:` | `Then the meta tag should exist with the following attributes:` | | `Then I should not see a meta tag with the following attributes:` | `Then the meta tag should not exist with the following attributes:` | |   | | diff --git a/README.md b/README.md index 0c121f0..c0e5309 100644 --- a/README.md +++ b/README.md @@ -54,195 +54,6 @@ Modification of `behat.yml` configuration is not required. - `\Exception` is thrown for all assertions. - `\RuntimeException` is thrown for any unfulfilled requirements within a step. -### Available steps - -For migration from v2 to v3, see [MIGRATION.md](MIGRATION.md). - -| Step Name | Description | -|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------| -| **[`ContentTrait`](src/ContentTrait.php) ([example](tests/behat/features/content.feature))** | | -| `Given no :type content type` | Delete the content type. | -| `When I visit :type :title` | Navigate to a page with a specified type and title. | -| `When I edit :type :title` | Navigate to the edit page with a specified type and title. | -| `When I delete :type :title` | Navigate to the delete page with a specified type and title. | -| `Given no ([a-zA-z0-9_-]+) content:$/` | Remove content defined by provided properties. | -| `When the moderation state of :type :title changes from :old_state to :new_state` | Change the moderation state of content with the specified title. | -| `When I visit :type :title scheduled transitions` | Visit the scheduled transition page for a node with the specified title. | -|   | | -| **[`CookieTrait`](src/CookieTrait.php) ([example](tests/behat/features/cookie.feature))** | | -| `Then a cookie with( the) name :name should exist` | Check if a cookie with the specified name exists. | -| `Then a cookie with( the) name :name and value :value should exist` | Check if a cookie with the specified name and value exists. | -| `Then a cookie with( the) name :name and value containing :partial_value should exist` | Check if a cookie with the specified name and a partial value exists. | -| `Then a cookie with( the) name containing :partial_name should exist` | Check if a cookie with a partial name exists. | -| `Then a cookie with( the) name containing :partial_name and value :value should exist` | Check if a cookie with a partial name and value exists. | -| `Then a cookie with( the) name containing :partial_name and value containing :partial_value should exist` | Check if a cookie with a partial name and partial value exists. | -| `Then a cookie with( the) name :name should not exist` | Check if a cookie with the specified name does not exist. | -| `Then a cookie with( the) name :name and value :value should not exist` | Check if a cookie with the specified name and value does not exist. | -| `Then a cookie with( the) name :name and value containing :partial_value should not exist` | Check if a cookie with the specified name and a partial value does not exist. | -| `Then a cookie with( the) name containing :partial_name should not exist` | Check if a cookie with a partial name does not exist. | -| `Then a cookie with( the) name containing :partial_name and value :value should not exist` | Check if a cookie with a partial name and value does not exist. | -| `Then a cookie with( the) name containing :partial_name and value containing :partial_value should not exist` | Check if a cookie with a partial name and partial value does not exist. | -|   | | -| **[`DraggableViewsTrait`](src/DraggableViewsTrait.php) ([example](tests/behat/features/draggableviews.feature))** | | -| `When I save the draggable views items of the view :view_id and the display :views_display_id for the :bundle content in the following order:` | Save the order of the draggable items. | -|   | | -| **[`EckTrait`](src/EckTrait.php) ([example](tests/behat/features/eck.feature))** | | -| `Given the following eck :bundle :entity_type entities exist:` | Create ECK entities. | -| `Given the following eck :bundle :entity_type entities do not exist:` | Remove custom entities by field. | -| `When I edit eck :bundle :entity_type entity with the title :title` | Navigate to the edit page for the specified ECK entity type and title. | -| `When I visit eck :bundle :entity_type entity with the title :title` | Navigate to the view page for the specified ECK entity type and title. | -|   | | -| **[`ElementTrait`](src/ElementTrait.php) ([example](tests/behat/features/element.feature))** | | -| `Then I( should) see the :selector element with the :attribute attribute set to :value` | Assert that an element with the specified selector and attribute value exists. | -| `I( should) see the :selector element with a(n) :attribute attribute containing :value` | Assert that an element with the specified selector and attribute value exists, matching a wildcard pattern. | -| `Then I should see an element :selector using :type contains :text text` | Assert that an element with selector contains text. | -|   | | -| **[`EmailTrait`](src/EmailTrait.php) ([example](tests/behat/features/email.feature))** | | -| `Given I enable the test email system` | Enable the test email system. | -| `Given I disable the test email system` | Disable the test email system. | -| `When I clear the test email system queue` | Clear the test email system queue. | -| `Then an email is sent to :address` | Assert that an email was sent to the specified address. | -| `Then no emails were sent` | Assert that no email messages were sent. | -| `Then no emails were sent to :address` | Assert that no email messages were sent to the specified address. | -| `Then an email header :header contains:` | Assert that an email message header contains the specified content. | -| `Then an email header :header contains exact:` | Assert that an email message header contains the exact specified content. | -| `Then /^an email to "(?P[^"]*)" user is "(?P[^"]*)" with "(?P[^"]*)" content:$/` | Assert that an email message was sent or not sent to a user with the specified content. | -| `Then an email :field contains` | Assert that an email message field contains the specified value. | -| `Then an email :field contains exact` | Assert that an email message field contains the exact specified value. | -| `Then an email :field does not contain` | Assert that an email message field does not contain the specified value. | -| `Then an email :field does not contains exact` | Assert that an email message field does not contain the exact specified value. | -| `When I follow the link number :number in the email with the subject` | Visit a link from the email with the specified subject. | -| `Then file :name attached to the email with the subject` | Assert that a file is attached to an email message with the specified subject. | -|   | | -| **[`FieldTrait`](src/FieldTrait.php) ([example](tests/behat/features/field.feature))** | | -| `Then I see field :name` | Assert that a field exists on the page using its id, name, label, or value. | -| `Then I don't see field :name` | Assert that a field does not exist on the page using its id, name, label, or value. | -| `Then field :name :exists on the page` | Assert whether the field exists on the page using its id, name, label, or value. | -| `Then field :name is :disabled on the page` | Assert whether the field is disabled on the page. | -| `Then field :name should be :presence on the page and have state :state` | Assert whether the field exists on the page and has a specified state. | -| `Then I fill color in :field for :value` | Fills value for color field. | -| `Then color field :field value is :value` | Asserts that a color field has a value. | -|   | | -| **[`FileDownloadTrait`](src/FileDownloadTrait.php) ([example](tests/behat/features/file-download.feature))** | | -| `Then I download file from :url` | Download a file from the specified URL. | -| `Then I download file from link :link` | Download a file from the specified HTML link. | -| `Then I see download :link link :presence(on the page)` | Assert that an HTML link is present or absent on the page. | -| `Then downloaded file contains:` | Assert the contents of the downloaded file. | -| `Then downloaded file name is :name` | Assert the file name of the downloaded file. | -| `Then downloaded file is zip archive that contains files:` | Assert that the downloaded file is a ZIP archive containing specified files. | -| `Then downloaded file is zip archive that does not contain files:` | Assert that the downloaded file is a ZIP archive that does not contain specified files. | -|   | | -| **[`FileTrait`](src/FileTrait.php) ([example](tests/behat/features/file.feature))** | | -| `Given managed file:` | Create a managed file with the properties provided in the table. | -| `Given no managed files:` | Delete managed files defined by the provided properties or fields. | -| `Given unmanaged file :uri created` | Create an unmanaged file. | -| `Given unmanaged file :uri created with content :content` | Create an unmanaged file with specified content. | -| `Then unmanaged file :uri exists` | Assert that an unmanaged file with the specified URI exists. | -| `Then unmanaged file :uri does not exist` | Assert that an unmanaged file with the specified URI does not exist. | -| `Then unmanaged file :uri has content :content` | Assert that an unmanaged file exists and has the specified content. | -| `Then unmanaged file :uri does not have content :content` | Assert that an unmanaged file exists and does not have the specified content. | -|   | | -| **[`JsTrait`](src/JsTrait.php) ([example](tests/behat/features/js.feature))** | | -| `When I accept confirmation dialogs` | Accept confirmation dialogs appearing on the page. | -| `When I do not accept confirmation dialogs` | Do not accept confirmation dialogs appearing on the page. | -| `When /^(?:\|I )click (an?\|on) "(?P[^"]*)" element$/` | Click on the element defined by the selector. | -| `When I trigger JS :event event on :selector element` | Trigger an event on the specified element. | -| `Then /^I scroll to an? element with id "([^"]*)"$/` | Scroll to an element with ID. | -| `Then the element with id :id should be at the top of the page` | Assert the element with id at the top of page. | -|   | | -| **[`KeyboardTrait`](src/KeyboardTrait.php) ([example](tests/behat/features/keyboard.feature))** | | -| `When I press the keys :keys` | Press multiple keyboard keys. | -| `When I press the keys :keys on the element :selector` | Press multiple keyboard keys on the specified element. | -| `When I press the key :char` | Press the specified keyboard key. | -| `When I press the key :char on the element :selector` | Press the specified keyboard key on the specified element. | -|   | | -| **[`LinkTrait`](src/LinkTrait.php) ([example](tests/behat/features/link.feature))** | | -| `Then I should see the link :text with :href` | Assert the presence of a link with the specified href. | -| `Then I should see the link :text with :href in :locator` | Assert the presence of a link with the specified href in the specified locator. | -| `Then I should not see the link :text with :href` | Assert that a link with the specified href does not exist. | -| `Then I should not see the link :text with :href in :locator` | Assert that a link with the specified href does not exist in the specified locator. | -| `Then the link with title :title exists` | Assert that a link with the specified title exists. | -| `Then the link with title :title does not exist` | Assert that a link with the specified title does not exist. | -| `Then I click the link with title :title` | Click on the link with the specified title. | -| `Then the link( with title) :text is an absolute link` | Assert that the link with the specified text is absolute. | -| `Then the link( with title) :text is not an absolute link` | Assert that the link with the specified text is not absolute. | -|   | | -| **[`MediaTrait`](src/MediaTrait.php) ([example](tests/behat/features/media.feature))** | | -| `Given no :type media type` | Remove the specified media type. | -| `Given :type media:` | Create media of the given type. | -| `Given /^no ([a-zA-z0-9_-]+) media:$/` | Remove media defined by the provided properties. | -| `Navigate to edit media with specified type and name.` | Navigate to the edit page for the specified media type and name. | -|   | | -| **[`MenuTrait`](src/MenuTrait.php) ([example](tests/behat/features/menu.feature))** | | -| `Given no menus:` | Remove the specified menus. | -| `Given menus:` | Create a menu if one does not exist. | -| `Given no :menu_name menu_links:` | Remove menu links by title. | -| `Given :menu_name menu_links:` | Create menu links. | -|   | | -| **[`MetaTagTrait`](src/MetaTagTrait.php) ([example](tests/behat/features/metatag.feature))** | | -| `Then the meta tag should exist with the following attributes:` | Assert that a meta tag with specific attributes and values exists. | -| `Then the meta tag should not exist with the following attributes:` | Assert that a meta tag with specific attributes and values does not exist. | -|   | | -| **[`ParagraphsTrait`](src/ParagraphsTrait.php) ([example](tests/behat/features/paragraphs.feature))** | | -| `Given the following fields for the paragraph :paragraph_type exist in the field :parent_field within the :parent_bundle :parent_entity_type identified by the field :parent_lookup_field and the value :parent_lookup_value:` | Create a paragraph of the given type with fields within an existing entity. | -|   | | -| **[`PathTrait`](src/PathTrait.php) ([example](tests/behat/features/path.feature))** | | -| `Then the path should be :path` | Assert the current page is the specified path. | -| `Then the path should not be :path` | Assert the current page is not the specified path. | -| `Given the basic authentication with the username :username and the password :password` | Set basic authentication for the current session. | -|   | | -| **[`ResponseTrait`](src/ResponseTrait.php) ([example](tests/behat/features/response.feature))** | | -| `Then the response should contain the header :header_name` | Assert that the response contains a header with the specified name. | -| `Then the response should not contain the header :name` | Assert that the response does not contain a header with the specified name. | -| `Then the response header :name should contain the value :value` | Assert that the response header contains the specified value. | -| `Then the response header :name should not contain the value :value` | Assert that the response header does not contain the specified value. | -|   | | -| **[`RoleTrait`](src/RoleTrait.php) ([example](tests/behat/features/role.feature))** | | -| `Given the role :role with the permissions :permissions` | Create a single role with the specified permissions. | -| `Given the following roles:` | Create multiple roles from the specified table. | -|   | | -| **[`SelectTrait`](src/SelectTrait.php) ([example](tests/behat/features/select.feature))** | | -| `Then select :select should have an option :option` | Assert that the specified select element has the specified option. | -| `Then select :select should not have an option :option` | Assert that the specified select element does not have the specified option. | -| `Then /^the option "([^"]*)" from select "([^"]*)" is selected$/` | Assert that the specified option is selected in the specified select element. | -|   | | -| **[`SearchApiTrait`](src/SearchApiTrait.php) ([example](tests/behat/features/search.feature))** | | -| `When I add the :content_type content with the title :title to the search index` | Index a node with all Search API indices. | -| `When I run search indexing for :count item(s)` | Index a specified number of items across all active Search API indices. | -|   | | -| **[`TaxonomyTrait`](src/TaxonomyTrait.php) ([example](tests/behat/features/taxonomy.feature))** | | -| `Given the following :vocabulary_machine_name vocabulary terms do not exist:` | Remove terms from the specified vocabulary. | -| `Then the vocabulary :machine_name with the name :name should exists` | Assert that the specified vocabulary exists. | -| `Then the vocabulary :machine_name should not exist` | Assert that the specified vocabulary does not exist. | -| `Then the taxonomy term :term_name from the vocabulary :vocabulary_machine_name should exist` | Assert that the specified taxonomy term exists by name. | -| `Then the taxonomy term :term_name from the vocabulary :vocabulary_machine_name should not exists` | Assert that the specified taxonomy term exists by name. | -| `When I visit the :vocabulary_machine_name vocabulary :term_name term page` | Visit the specified vocabulary term page. | -| `When I edit the :vocabulary_machine_name vocabulary :term_name term page` | Visit the specified vocabulary term edit page. | -|   | | -| **[`UserTrait`](src/UserTrait.php) ([example](tests/behat/features/user.feature))** | | -| `When I visit user :name profile` | Visit the profile page of the specified user. | -| `When I go to my profile edit page` | Visit the edit page of the current user. | -| `When I edit user :name profile` | Visit the edit page of the specified user. | -| `Given no users:` | Remove users specified in the table. | -| `Then user :name has :roles role(s) assigned` | Assert that a user has the specified roles assigned. | -| `Then user :name does not have :roles role(s) assigned` | Assert that a user does not have the specified roles assigned. | -| `Then user :name has :status status` | Assert whether a user is active or not. | -| `Then I set user :user password to :password` | Set a password for a user. | -| `Then the last access time of user :name is :time` | Set last access time for user. | -|   | | -| **[`VisibilityTrait`](src/VisibilityTrait.php) ([example](tests/behat/features/visibility.feature))** | | -| `Then /^(?:\|I )should see a visible "(?P[^"]*)" element$/` | Assert that the element with the specified CSS selector is visible on the page. | -| `Then /^(?:\|I )should not see a visible "(?P[^"]*)" element$/` | Assert that the element with the specified CSS selector is not visible on the page. | -| `Then /^(?:\|I )should see a visually visible "(?P[^"]*)" element(?: with top offset of "([^"]*)" pixels)?$/` | Assert that the element with the specified CSS selector is visually visible on the page. | -| `Then /^(?:\|I )should not see a visually hidden "(?P[^"]*)" element(?: with top offset of "([^"]*)" pixels)?$/` | Assert that the element with the specified CSS selector is visually hidden on the page. | -|   | | -| **[`WaitTrait`](src/WaitTrait.php) ([example](tests/behat/features/wait.feature))** | | -| `When I wait for :number second(s)` | Wait for a specified number of seconds. | -| `When I wait for :number second(s) for AJAX to finish` | Wait for the AJAX calls to finish. | -|   | | -| **[`WysiwygTrait`](src/WysiwygTrait.php) ([example](tests/behat/features/wysiwyg.feature))** | | -| `When I fill in WYSIWYG :field with :value` | Set the value for the WYSIWYG field. | - #### Skipping before scenario hooks Some traits provide `beforeScenario` hook implementations. These can be disabled @@ -251,12 +62,1095 @@ by adding `behat-steps-skip:METHOD_NAME` tag to your test. For example, to skip `beforeScenario` hook from `JsTrait`, add `@behat-steps-skip:jsBeforeScenarioInit` tag to the feature. +## Available steps + +- [ContentTrait](#contenttrait) + +- [CookieTrait](#cookietrait) + +- [EckTrait](#ecktrait) + +- [DraggableviewsTrait](#draggableviewstrait) + +- [EmailTrait](#emailtrait) + +- [ElementTrait](#elementtrait) + +- [FieldTrait](#fieldtrait) + +- [FileDownloadTrait](#filedownloadtrait) + +- [FileTrait](#filetrait) + +- [JsTrait](#jstrait) + +- [KeyboardTrait](#keyboardtrait) + +- [LinkTrait](#linktrait) + +- [MediaTrait](#mediatrait) + +- [MenuTrait](#menutrait) + +- [ParagraphsTrait](#paragraphstrait) + +- [PathTrait](#pathtrait) + +- [ResponseTrait](#responsetrait) + +- [RoleTrait](#roletrait) + +- [SelectTrait](#selecttrait) + +- [SearchApiTrait](#searchapitrait) + +- [TaxonomyTrait](#taxonomytrait) + +- [UserTrait](#usertrait) + +- [VisibilityTrait](#visibilitytrait) + +- [WaitTrait](#waittrait) + +- [WysiwygTrait](#wysiwygtrait) + + +### ContentTrait + +[Source](src/ContentTrait.php), [Example](tests/behat/features/content.feature) + +#### Delete content type + +```gherkin +@Given no :type content type +``` +Example: +```gherkin +Given no "article" content type +``` + +#### Remove content defined by provided properties + +```gherkin +@Given /^no ([a-zA-z0-9_-]+) content:$/ +``` +Example: +```gherkin +Given no "article" content: + | title | + | Test article | + | Another test article | +``` + +#### Navigate to page with specified type and title + +```gherkin +@When I visit :type :title +``` +Example: +```gherkin +When I visit "article" "Test article" +``` + +#### Navigate to edit page with specified type and title + +```gherkin +@When I edit :type :title +``` +Example: +```gherkin +When I edit "article" "Test article" +``` + +#### Navigate to delete page with specified type and title + +```gherkin +@When I delete :type :title +``` + +#### Change moderation state of a content with specified title + +```gherkin +@When the moderation state of :type :title changes from :old_state to :new_state +``` +Example: +```gherkin +When the moderation state of "article" "Test article" changes from "draft" to "published" +``` + +#### Visit scheduled-transition page for node with title + +```gherkin +@When I visit :type :title scheduled transitions +``` + +### CookieTrait + +[Source](src/CookieTrait.php), [Example](tests/behat/features/cookie.feature) + +#### Check if a cookie exists + +```gherkin +@Then a cookie with( the) name :name should exist +``` + +#### Check if a cookie exists with a specific value + +```gherkin +@Then a cookie with( the) name :name and value :value should exist +``` + +#### Check if a cookie exists with a value containing a partial value + +```gherkin +@Then a cookie with( the) name :name and value containing :partial_value should exist +``` + +#### Check if a cookie with a partial name exists + +```gherkin +@Then a cookie with( the) name containing :partial_name should exist +``` + +#### Check if a cookie with a partial name and value exists + +```gherkin +@Then a cookie with( the) name containing :partial_name and value :value should exist +``` + +#### Check if a cookie with a partial name and partial value exists + +```gherkin +@Then a cookie with( the) name containing :partial_name and value containing :partial_value should exist +``` + +#### Check if a cookie does not exist + +```gherkin +@Then a cookie with( the) name :name should not exist +``` + +#### Check if a cookie with a specific value does not exist + +```gherkin +@Then a cookie with( the) name :name and value :value should not exist +``` + +#### Check if a cookie with a value containing a partial value does not exist + +```gherkin +@Then a cookie with( the) name :name and value containing :partial_value should not exist +``` + +#### Check if a cookie with a partial name does not exist + +```gherkin +@Then a cookie with( the) name containing :partial_name should not exist +``` + +#### Check if a cookie with a partial name and value does not exist + +```gherkin +@Then a cookie with( the) name containing :partial_name and value :value should not exist +``` + +#### Check if a cookie with a partial name and partial value does not exist + +```gherkin +@Then a cookie with( the) name containing :partial_name and value containing :partial_value should not exist +``` + +### EckTrait + +[Source](src/EckTrait.php), [Example](tests/behat/features/eck.feature) + +#### Create eck entities + +```gherkin +@Given the following eck :bundle :entity_type entities exist: +``` +Example: +```gherkin +Given the following eck "contact" "contact_type" entities exist: +| title | field_marine_animal | field_fish_type | ... | +| Snook | Fish | Marine fish | 10 | +| ... | ... | ... | ... | +``` + +#### Remove custom entities by field + +```gherkin +@Given the following eck :bundle :entity_type entities do not exist: +``` +Example: +```gherkin +Given the following eck "contact" "contact_type" entities do not exist: +| field | value | +| field_a | Entity label | +``` + +#### Navigate to view entity page with specified type and title + +```gherkin +@When I visit eck :bundle :entity_type entity with the title :title +``` +Example: +```gherkin +When I visit eck "contact" "contact_type" entity with the title "Test contact" +``` + +#### Navigate to edit eck entity page with specified type and title + +```gherkin +@When I edit eck "contact" "contact_type" entity with the title "Test contact" +``` +```gherkin +@When I edit eck :bundle :entity_type entity with the title :title +``` + +### DraggableviewsTrait + +[Source](src/DraggableviewsTrait.php), [Example](tests/behat/features/draggableviews.feature) + +#### Save order of the Draggable Order items + +```gherkin +@When I save the draggable views items of the view :view_id and the display :views_display_id for the :bundle content in the following order: +``` + +### EmailTrait + +[Source](src/EmailTrait.php), [Example](tests/behat/features/email.feature) + +#### Clear test email system queue + +```gherkin +@When I clear the test email system queue +``` + +#### Visit a link from the email + +```gherkin +@When I follow the link number :number in the email with the subject +``` +```gherkin +@When I follow the link number :number in the email with the subject: +``` + +#### Assert that an email was sent to an address + +```gherkin +@Then an email is sent to :address +``` + +#### Assert that no email messages were sent + +```gherkin +@Then no emails were sent +``` + +#### Assert that no email messages were sent to a specified address + +```gherkin +@Then no emails were sent to :address +``` + +#### Assert that an email message header contains specified content + +```gherkin +@Then an email header :header contains: +``` + +#### Assert that an email message header contains exact specified content + +```gherkin +@Then an email header :header contains exact: +``` + +#### Assert that an email message was sent or not sent to a user with content + +```gherkin +@Then /^an email to "(?P[^"]*)" user is "(?P[^"]*)" with "(?P[^"]*)" content:$/ +``` + +#### Assert that an email message field contains a value + +```gherkin +@Then an email :field contains +``` +```gherkin +@Then an email :field contains: +``` + +#### Assert that an email message field contains an exact value + +```gherkin +@Then an email :field contains exact +``` +```gherkin +@Then an email :field contains exact: +``` + +#### Assert that an email message field does not contain a value + +```gherkin +@Then an email :field does not contain +``` +```gherkin +@Then an email :field does not contain: +``` + +#### Assert that an email message field does not contain an exact value + +```gherkin +@Then an email :field does not contain exact +``` +```gherkin +@Then an email :field does not contain exact: +``` + +#### Assert that a file is attached to an email message with specified subject + +```gherkin +@Then file :name attached to the email with the subject +``` +```gherkin +@Then file :name attached to the email with the subject: +``` + +### ElementTrait + +[Source](src/ElementTrait.php), [Example](tests/behat/features/element.feature) + +#### Assert an element with selector and attribute with a value exists + +```gherkin +@Then the element :selector with the attribute :attribute and the value :value should exist +``` + +#### Assert an element with selector and attribute containing a value exists + +```gherkin +@Then the element :selector with the attribute :attribute and the value containing :value should exist +``` + +#### Assert an element with selector and attribute with a value exists + +```gherkin +@Then the element :selector with the attribute :attribute and the value :value should not exist +``` + +#### Assert an element with selector and attribute containing a value does not exist + +```gherkin +@Then the element :selector with the attribute :attribute and the value containing :value should not exist +``` + +### FieldTrait + +[Source](src/FieldTrait.php), [Example](tests/behat/features/field.feature) + +#### Assert that field exists on the page using id,name,label or value + +```gherkin +@Then I see field :name +``` +Example: +```gherkin +Then I see field "Body" +Then I see field "field_body" +``` + +#### Assert that field does not exist on the page using id,name,label or value + +```gherkin +@Then I don't see field :name +``` +Example: +```gherkin +Then I don't see field "Body" +Then I don't see field "field_body" +``` + +#### Assert whether the field exists on the page using id,name,label or value + +```gherkin +@Then field :name :exists on the page +``` +Example: +```gherkin +Then field "Body" "exists" on the page +Then field "field_body" "exists" on the page +Then field "Tags" "does not exist" on the page +Then field "field_tags" "does not exist" on the page +``` + +#### Assert whether the field has a state + +```gherkin +@Then field :name is :disabled on the page +``` +Example: +```gherkin +Then field "Body" is "disabled" on the page +Then field "field_body" is "disabled" on the page +Then field "Tags" is "enabled" on the page +Then field "field_tags" is "not enabled" on the page +``` + +#### Assert whether the field exists on the page and has a state + +```gherkin +@Then field :name should be :presence on the page and have state :state +``` +Example: +```gherkin +Then field "Body" should be "present" on the page and have state "enabled" +Then field "Tags" should be "absent" on the page and have state "n/a" +``` + +### FileDownloadTrait + +[Source](src/FileDownloadTrait.php), [Example](tests/behat/features/file_download.feature) + +#### Download a file from the specified URL + +```gherkin +@Then I download file from :url +``` + +#### Download the file from the specified HTML link + +```gherkin +@Then I download file from link :link +``` + +#### Assert that an HTML link is present or absent on the page + +```gherkin +@Then I see download :link link :presence(on the page) +``` + +#### Assert the contents of the download file + +```gherkin +@Then downloaded file contains: +``` + +#### Assert the file name of the downloaded file + +```gherkin +@Then downloaded file name is :name +``` + +#### Assert downloaded file is a ZIP archive and it contains files + +```gherkin +@Then downloaded file is zip archive that contains files: +``` + +#### Assert downloaded file is a ZIP archive and it does not contain files + +```gherkin +@Then downloaded file is zip archive that does not contain files: +``` + +### FileTrait + +[Source](src/FileTrait.php), [Example](tests/behat/features/file.feature) + +#### Create managed file with properties provided in the table + +```gherkin +@Given managed file: +``` + +#### Delete managed files defined by provided properties/fields + +```gherkin +@Given no managed files: +``` +Example: +```gherkin +Given no managed files: +| filename | +| myfile.jpg | +| otherfile.jpg | + Given no managed files: + | uri | + | public://myfile.jpg | + | public://otherfile.jpg | +``` + +#### Create an unmanaged file with specified content + +```gherkin +@Given unmanaged file :uri created +``` + +#### Create an unmanaged file with specified content + +```gherkin +@Given unmanaged file :uri created with content :content +``` + +#### Assert that an unmanaged file with specified URI exists + +```gherkin +@Then unmanaged file :uri exists +``` + +#### Assert that an unmanaged file with specified URI does not exist + +```gherkin +@Then unmanaged file :uri does not exist +``` + +#### Assert that an unmanaged file exists and has specified content + +```gherkin +@Then unmanaged file :uri has content :content +``` + +#### Assert that an unmanaged file exists and does not have specified content + +```gherkin +@Then unmanaged file :uri does not have content :content +``` + +### JsTrait + +[Source](src/JsTrait.php), [Example](tests/behat/features/js.feature) + +#### Accept confirmation dialogs appearing on the page + +```gherkin +@When I accept confirmation dialogs +``` +Example: +```gherkin +When I accept confirmation dialogs +``` + +#### Do not accept confirmation dialogs appearing on the page + +```gherkin +@When I do not accept confirmation dialogs +``` +Example: +```gherkin +When I do not accept confirmation dialogs +``` + +#### Click on the element defined by the selector + +```gherkin +@When /^(?:|I )click (an?|on) "(?P[^"]*)" element$/ +``` +Example: +```gherkin +When I click on ".button" element +When I click ".button" element +When click ".button" element +``` + +#### Trigger an event on the specified element + +```gherkin +@When I trigger JS :event event on :selector element +``` + +### KeyboardTrait + +[Source](src/KeyboardTrait.php), [Example](tests/behat/features/keyboard.feature) + +#### Press multiple keyboard keys, optionally on element + +```gherkin +@When I press the keys :keys +``` +```gherkin +@When I press the keys :keys on the element :selector +``` + +#### Press keyboard key, optionally on element + +```gherkin +@When I press the key :char +``` +```gherkin +@When I press the key :char on the element :selector +``` + +### LinkTrait + +[Source](src/LinkTrait.php), [Example](tests/behat/features/link.feature) + +#### Assert presence of a link with a href + +```gherkin +@Then I should see the link :text with :href +``` +```gherkin +@Then I should see the link :text with :href in :locator +``` +Example: +```gherkin +Then I should see the link "About us" with "/about-us" +Then I should see the link "About us" with "/about-us" in ".main-nav" +Then I should see the link "About us" with "/about*" in ".main-nav" +``` + +#### Assert link with a href does not exist + +```gherkin +@Then I should not see the link :text with :href +``` +```gherkin +@Then I should not see the link :text with :href in :locator +``` +Example: +```gherkin +Then I should not see the link "About us" with "/about-us" +Then I should not see the link "About us" with "/about-us" in ".main-nav" +Then I should not see the link "About us" with "/about*" in ".main-nav" +``` + +#### Assert that a link with a title exists + +```gherkin +@Then the link with title :title exists +``` + +#### Assert that a link with a title does not exist + +```gherkin +@Then the link with title :title does not exist +``` + +#### Click on the link with a title + +```gherkin +@Then I click the link with title :title +``` + +### MediaTrait + +[Source](src/MediaTrait.php), [Example](tests/behat/features/media.feature) + +#### Remove media type + +```gherkin +@Given no "video" media type +``` +```gherkin +@Given no :type media type +``` + +#### Creates media of a given type + +```gherkin +@Given :type media: +``` +Example: +```gherkin +Given "video" media: +| name | field1 | field2 | field3 | +| My media | file.jpg | value | value | +| ... | ... | ... | ... | +``` + +#### Remove media defined by provided properties + +```gherkin +@Given /^no ([a-zA-z0-9_-]+) media:$/ +``` +Example: +```gherkin +Given no "image" media: +| name | +| Media item | +| Another media item | +``` + +#### Navigate to edit media with specified type and name + +```gherkin +@When I edit :type media :name +``` +Example: +```gherkin +When I edit "document" media "Test document" +``` + +### MenuTrait + +[Source](src/MenuTrait.php), [Example](tests/behat/features/menu.feature) + +#### Remove menu by menu name + +```gherkin +@Given no menus: +``` + +#### Create a menu if one does not exist + +```gherkin +@Given menus: +``` + +#### Remove menu links by title + +```gherkin +@Given no :menu_name menu_links: +``` + +#### Create menu links + +```gherkin +@Given :menu_name menu_links: +``` + +### ParagraphsTrait + +[Source](src/ParagraphsTrait.php), [Example](tests/behat/features/paragraphs.feature) + +#### Create a paragraph of the given type with fields within an existing entity + +```gherkin +@Given the following fields for the paragraph :paragraph_type exist in the field :parent_field within the :parent_bundle :parent_entity_type identified by the field :parent_lookup_field and the value :parent_lookup_value: +``` +Example: +```gherkin +Given the following fields for the paragraph "text" exist in the field "field_component" within the "landing_page" "node" identified by the field "title" and the value "My landing page": +| field_paragraph_title | My paragraph title | +| field_paragraph_longtext:value | My paragraph message | +| field_paragraph_longtext:format | full_html | +| ... | ... | +``` + +### PathTrait + +[Source](src/PathTrait.php), [Example](tests/behat/features/path.feature) + +#### Set basic authentication for the current session + +```gherkin +@Given the basic authentication with the username :username and the password :password +``` +Example: +```gherkin +Given the basic authentication with the username "myusername" and the password "mypassword" +``` + +#### Assert that the current page is a specified path + +```gherkin +@Then the path should be :path +``` +Example: +```gherkin +Then the path should be "/about-us" +Then the path should be "" +``` + +#### Assert that the current page is not a specified path + +```gherkin +@Then the path should not be :path +``` +Example: +```gherkin +Then the path should not be "/about-us" +Then the path should not be "" +``` + +### ResponseTrait + +[Source](src/ResponseTrait.php), [Example](tests/behat/features/response.feature) + +#### Assert that a response contains a header with specified name + +```gherkin +@Then the response should contain the header :header_name +``` +Example: +```gherkin +Then the response should contain the header "Connection" +``` + +#### Assert that a response does not contain a header with a specified name + +```gherkin +@Then the response should not contain the header :header_name +``` +Example: +```gherkin +Then the response should not contain the header "Connection" +``` + +#### Assert that a response contains a header with a specified name and value + +```gherkin +@Then the response header :header_name should contain the value :header_value +``` +Example: +```gherkin +Then the response header "Connection" should contain the value "Keep-Alive" +``` + +#### Assert a response does not contain a header with a specified name and value + +```gherkin +@Then the response header :header_name should not contain the value :header_value +``` +Example: +```gherkin +Then the response header "Connection" should not contain the value "Keep-Alive" +``` + +### RoleTrait + +[Source](src/RoleTrait.php), [Example](tests/behat/features/role.feature) + +#### Create a single role with specified permissions + +```gherkin +@Given the role :role_name with the permissions :permissions +``` + +#### Create multiple roles from the specified table + +```gherkin +@Given the following roles: +``` + +### SelectTrait + +[Source](src/SelectTrait.php), [Example](tests/behat/features/select.feature) + +#### Assert that a select has an option + +```gherkin +@Then select :select should have an option :option +``` + +#### Assert that a select does not have an option + +```gherkin +@Then select :select should not have an option :option +``` + +#### Assert that a select option is selected + +```gherkin +@Then /^the option "([^"]*)" from select "([^"]*)" is selected$/ +``` + +### SearchApiTrait + +[Source](src/SearchApiTrait.php), [Example](tests/behat/features/search_api.feature) + +#### Index a node of a specific content type with a specific title + +```gherkin +@When I add the :content_type content with the title :title to the search index +``` + +#### Run indexing for a specific number of items + +```gherkin +@When I run search indexing for :count item(s) +``` + +### TaxonomyTrait + +[Source](src/TaxonomyTrait.php), [Example](tests/behat/features/taxonomy.feature) + +#### Remove terms from a specified vocabulary + +```gherkin +@Given the following :vocabulary_machine_name vocabulary terms do not exist: +``` +Example: +```gherkin +Given the following "fruits" vocabulary terms do not exist: + | Apple | + | Pear | +``` + +#### Visit specified vocabulary term page + +```gherkin +@When I visit the :vocabulary_machine_name vocabulary :term_name term page +``` +Example: +```gherkin +When I visit the "fruits" vocabulary "Apple" term page +``` + +#### Edit specified vocabulary term page + +```gherkin +@When I edit the :vocabulary_machine_name vocabulary :term_name term page +``` +Example: +```gherkin +When I edit the "fruits" vocabulary "Apple" term page +``` + +#### Assert that a vocabulary with a specific name exists + +```gherkin +@Then the vocabulary :machine_name with the name :name should exist +``` +Example: +```gherkin +Then the vocabulary "topics" with the name "Topics" should exist +``` + +#### Assert that a vocabulary with a specific name does not exist + +```gherkin +@Then the vocabulary :machine_name should not exist +``` +Example: +```gherkin +Then the vocabulary "topics" should not exist +``` + +#### Assert that a taxonomy term exist by name + +```gherkin +@Then the taxonomy term :term_name from the vocabulary :vocabulary_machine_name should exist +``` +Example: +```gherkin +Then the taxonomy term "Apple" from the vocabulary "Fruits" should exist +``` + +#### Assert that a taxonomy term does not exist by name + +```gherkin +@Then the taxonomy term :term_name from the vocabulary :vocabulary_machine_name should not exist +``` +Example: +```gherkin +Then the taxonomy term "Apple" from the vocabulary "Fruits" should not exist +``` + +### UserTrait + +[Source](src/UserTrait.php), [Example](tests/behat/features/user.feature) + +#### Remove users specified in the table + +```gherkin +@Given no users: +``` + +#### Visit profile page of the specified user + +```gherkin +@When I visit user :name profile +``` + +#### Visit edit page of the current user + +```gherkin +@When I go to my profile edit page +``` + +#### Visit edit page of the specified user + +```gherkin +@When I edit user :name profile +``` + +#### Assert that a user has roles assigned + +```gherkin +@Then user :name has :roles role(s) assigned +``` + +#### Assert that a user does not have roles assigned + +```gherkin +@Then user :name does not have :roles role(s) assigned +``` + +#### Assert that a user is active or not + +```gherkin +@Then user :name has :status status +``` + +#### Set a password for a user + +```gherkin +@Then I set user :user password to :password +``` + +### VisibilityTrait + +[Source](src/VisibilityTrait.php), [Example](tests/behat/features/visibility.feature) + +#### Assert that element with specified CSS is visible on page + +```gherkin +@Then /^(?:|I )should see a visible "(?P[^"]*)" element$/ +``` + +#### Assert that element with specified CSS is visible on page + +```gherkin +@Then /^(?:|I )should not see a visible "(?P[^"]*)" element$/ +``` + +#### Assert that element with specified CSS is visually visible on page + +```gherkin +@Then /^(?:|I )should see a visually visible "(?P[^"]*)" element(?: with top offset of "([^"]*)" pixels)?$/ +``` + +#### Assert that element with specified CSS is visually hidden on page + +```gherkin +@Then /^(?:|I )should not see a visually hidden "(?P[^"]*)" element(?: with top offset of "([^"]*)" pixels)?$/ +``` + +### WaitTrait + +[Source](src/WaitTrait.php), [Example](tests/behat/features/wait.feature) + +#### Wait for a specified number of seconds + +```gherkin +@When I wait for :seconds second(s) +``` + +#### Wait for the AJAX calls to finish + +```gherkin +@When I wait for :seconds second(s) for AJAX to finish +``` + +### WysiwygTrait + +[Source](src/WysiwygTrait.php), [Example](tests/behat/features/wysiwyg.feature) + +#### Set value for WYSIWYG field + +```gherkin +@When /^(?:|I )fill in WYSIWYG "(?P(?:[^"]|\")*)" with "(?P(?:[^"]|\")*)"$/ +``` + + + ## Development ### Local environment setup -- - Install [Docker](https://www.docker.com/), [Pygmy](https://github.com/pygmystack/pygmy), [Ahoy](https://github.com/ahoy-cli/ahoy) and shut down local web services (Apache/Nginx, MAMP etc) @@ -301,5 +1195,11 @@ ahoy test-bdd -- --tags=wip # Run all scenarios tagged with `@wip` tag - `ahoy update-fixtures` to copy configuration changes from build directory to the fixtures directory +#### Updating documentation + +``` +php docs.php +``` + --- _Repository created using https://getscaffold.dev/ project scaffold template_ diff --git a/docs.php b/docs.php new file mode 100644 index 0000000..32525ac --- /dev/null +++ b/docs.php @@ -0,0 +1,359 @@ + $exclude + * Array of trait names to exclude. + * + * @return array|string>>> + * Array of info with 'name', 'steps', 'description', and 'example' keys. + * + * @throws \ReflectionException + */ +function extract_info(string $class_name, array $exclude = []): array { + $reflection = new ReflectionClass($class_name); + + $traits = $reflection->getTraits(); + + $result = []; + foreach ($traits as $trait) { + $trait_name = $trait->getShortName(); + + if (in_array($trait_name, $exclude, TRUE)) { + continue; + } + + $trait_prefix = str_replace('Trait', '', $trait_name); + + $methods = $trait->getMethods(ReflectionMethod::IS_PUBLIC); + + $info = []; + foreach ($methods as $method) { + if (!str_starts_with(strtolower($method->getName()), strtolower($trait_prefix))) { + continue; + } + + $comment = $method->getDocComment(); + + if ($comment) { + $parsed = parse_method_comment($comment); + if ($parsed) { + $info[] = $parsed + ['name' => $method->getName()]; + } + } + } + + if (!empty($info)) { + // Sort info by Given, When, Then. + usort($info, static function (array $a, array $b): int { + $order = ['@Given', '@When', '@Then']; + + $get_order_index = function ($step) use ($order): int { + foreach ($order as $index => $prefix) { + if (str_starts_with($step, $prefix)) { + return $index; + } + } + + return PHP_INT_MAX; + }; + + $a_step = $a['steps'][0] ?? ''; + $b_step = $b['steps'][0] ?? ''; + + $a_index = $get_order_index($a_step); + $b_index = $get_order_index($b_step); + + return $a_index <=> $b_index; + }); + + $result[$trait->getShortName()] = $info; + } + } + + return $result; +} + +/** + * Parse comment. + * + * @param string $comment + * The comment. + * + * @return array|string>|null + * Array of 'steps', 'description', and 'example' keys or NULL if steps were + * not found in the comment. + */ +function parse_method_comment(string $comment): ?array { + $return = [ + 'steps' => [], + 'description' => '', + 'example' => '', + ]; + + $lines = explode(PHP_EOL, $comment); + + $example_start = FALSE; + foreach ($lines as $line) { + $line = str_replace('/*', '', $line); + $line = str_replace('/**', '', $line); + $line = str_replace('*/', '', $line); + $line = preg_replace('/^\s*\*/', '', $line); + $line = rtrim((string) $line, " \t\n\r\0\x0B"); + // All docblock lines start with a space. + $line = substr($line, 1); + + if (str_starts_with($line, '@code')) { + $example_start = TRUE; + } + elseif (str_starts_with($line, '@endcode')) { + $example_start = FALSE; + } + elseif (str_starts_with($line, '@Given') || str_starts_with($line, '@When') || str_starts_with($line, '@Then')) { + $line = trim($line, " \t\n\r\0\x0B"); + $return['steps'][] = $line; + } + else { + if (!$example_start && empty($line)) { + continue; + } + + if ($example_start) { + $line = rtrim($line, "\t\n\r\0\x0B"); + $return['example'] .= $line . PHP_EOL; + } + + if (empty($return['description'])) { + $line = trim($line); + $return['description'] .= $line . ' '; + } + } + } + + if ($example_start) { + throw new \Exception('Example not closed'); + } + + if (!empty($return['steps'])) { + // Sort the steps by Given, When, Then. + $sorted = []; + foreach (['@Given', '@When', '@Then'] as $step) { + foreach ($return['steps'] as $step_item) { + if (str_starts_with($step_item, $step)) { + $sorted[] = $step_item; + } + } + } + $return['steps'] = $sorted; + + $return['description'] = trim($return['description']); + } + + return empty($return['steps']) ? NULL : $return; +} + +/** + * Convert info to content. + * + * @param array|string>>> $info + * Array of info items with 'name', 'from', and 'to' keys. + * + * @return string + * Markdown table. + */ +function info_to_content(array $info): string { + $output = ''; + + $index_output = ''; + + foreach ($info as $trait => $methods) { + $src_file = sprintf('src/%s.php', $trait); + + if (!file_exists($src_file)) { + throw new \Exception(sprintf('Source file %s does not exist', $src_file)); + } + + $example_name = camel_to_snake(str_replace('Trait', '', $trait)); + $example_file = sprintf('tests/behat/features/%s.feature', $example_name); + + if (!file_exists($example_file)) { + throw new \Exception(sprintf('Example file %s does not exist', $example_file)); + } + + $output .= sprintf('### %s', $trait) . PHP_EOL . PHP_EOL; + $output .= sprintf('[Source](%s), [Example](%s)', $src_file, $example_file) . PHP_EOL . PHP_EOL; + + $index_output .= sprintf('- [%s](#%s)' . PHP_EOL, $trait, strtolower($trait)) . PHP_EOL; + + foreach ($methods as $method) { + $method['steps'] = is_array($method['steps']) ? $method['steps'] : [$method['steps']]; + $method['description'] = is_string($method['description']) ? $method['description'] : ''; + $method['example'] = is_string($method['example']) ? $method['example'] : ''; + + $steps = array_reduce($method['steps'], function (string $carry, $item): string { + return $carry . sprintf("```gherkin\n%s\n```\n", $item); + }, ''); + + $steps = rtrim($steps, "\n"); + + $description = rtrim($method['description'], '.'); + + $output .= '#### ' . $description . PHP_EOL . PHP_EOL; + $output .= $steps . PHP_EOL; + + if (!empty($method['example'])) { + $example = sprintf("```gherkin\n%s```", $method['example']); + $output .= 'Example:' . PHP_EOL; + $output .= $example . PHP_EOL; + } + + $output .= PHP_EOL; + } + } + + return $index_output . PHP_EOL . $output; +} + +/** + * Print report. + * + * @param array|string>>> $info + * Array of info items with 'name', 'from', and 'to' keys. + */ +function print_report(array $info): void { + printf('Report:' . PHP_EOL); + + foreach ($info as $trait => $methods) { + foreach ($methods as $method) { + $method['steps'] = is_array($method['steps']) ? $method['steps'] : [$method['steps']]; + $method['name'] = is_string($method['name']) ? $method['name'] : ''; + $method['description'] = is_string($method['description']) ? $method['description'] : ''; + $method['example'] = is_string($method['example']) ? $method['example'] : ''; + + $step = $method['steps'][0]; + + if (str_starts_with((string) $step, '@When') && !str_contains((string) $method['steps'][0], 'I ')) { + printf(' %s::%s - %s' . PHP_EOL, $trait, $method['name'], 'Missing "I " in the step'); + } + + if (str_starts_with((string) $step, '@Then') && !str_contains((string) $method['steps'][0], 'should')) { + printf(' %s::%s - %s' . PHP_EOL, $trait, $method['name'], 'Missing "should" in the step'); + } + + if (str_starts_with((string) $step, '@Then') && !str_contains((string) $method['steps'][0], 'the')) { + printf(' %s::%s - %s' . PHP_EOL, $trait, $method['name'], 'Missing "the" in the step'); + } + + if (empty($method['example'])) { + printf(' %s::%s - Missing example' . PHP_EOL, $trait, $method['name']); + } + } + } +} + +/** + * Convert a string to snake case. + * + * @param string $string + * The string to convert. + * @param string $separator + * The separator. + * + * @return string + * The converted string. + */ +function camel_to_snake(string $string, string $separator = '_'): string { + $string = preg_replace_callback('/([^0-9])(\d+)/', static function (array $matches) use ($separator): string { + return $matches[1] . $separator . $matches[2]; + }, $string); + + $replacements = []; + foreach (mb_str_split((string) $string) as $key => $char) { + $lower_case_char = mb_strtolower($char); + if ($lower_case_char !== $char && $key !== 0) { + $replacements[$char] = $separator . $char; + } + } + $string = str_replace(array_keys($replacements), array_values($replacements), (string) $string); + + $string = trim($string, $separator); + + return mb_strtolower($string); +} + +/** + * Replace content in a string. + * + * @param string $haystack + * The content to search and replace in. + * @param string $start + * The start of the content to replace. + * @param string $end + * The end of the content to replace. + * @param string $replacement + * The replacement content. + */ +function replace_content(string $haystack, string $start, string $end, string $replacement): string { + if (!str_contains($haystack, $start)) { + throw new \Exception('Start not found in the haystack'); + } + + if (!str_contains($haystack, $end)) { + throw new \Exception('End not found in the haystack'); + } + + // Start should be before the end. + if (strpos($haystack, $start) > strpos($haystack, $end)) { + throw new \Exception('Start is after the end'); + } + + $pattern = '/' . preg_quote($start, '/') . '.*?' . preg_quote($end, '/') . '/s'; + $replacement = $start . PHP_EOL . $replacement . PHP_EOL . $end; + + return (string) preg_replace($pattern, $replacement, $haystack); +} diff --git a/phpcs.xml b/phpcs.xml index 20ab6cc..eeb6812 100644 --- a/phpcs.xml +++ b/phpcs.xml @@ -22,6 +22,7 @@ ../src ../tests/behat/bootstrap + ../docs.php ../circle\.yml diff --git a/phpstan.neon b/phpstan.neon index 25b174c..fa1d40d 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -11,6 +11,7 @@ parameters: paths: - ../src - ../tests/behat/bootstrap + - ../docs.php excludePaths: - vendor/* diff --git a/rector.php b/rector.php index f55c146..0a6c6ce 100644 --- a/rector.php +++ b/rector.php @@ -43,6 +43,7 @@ $rectorConfig->paths([ $drupalRoot . '/../../src', $drupalRoot . '/../../tests/behat/bootstrap', + $drupalRoot . '/../../docs.php', ]); $rectorConfig->sets([ diff --git a/src/ContentTrait.php b/src/ContentTrait.php index baa293e..f26082c 100644 --- a/src/ContentTrait.php +++ b/src/ContentTrait.php @@ -106,9 +106,9 @@ public function contentDeletePageWithTitle(string $type, string $title): void { * * @code * Given no "article" content: - * | title | - * | Test article | - * | Another test article | + * | title | + * | Test article | + * | Another test article | * @endcode * * @Given /^no ([a-zA-z0-9_-]+) content:$/ diff --git a/src/DraggableViewsTrait.php b/src/DraggableviewsTrait.php similarity index 97% rename from src/DraggableViewsTrait.php rename to src/DraggableviewsTrait.php index 19dea97..ec289ee 100644 --- a/src/DraggableViewsTrait.php +++ b/src/DraggableviewsTrait.php @@ -11,13 +11,13 @@ use Drupal\node\NodeInterface; /** - * Trait DraggableViewsTrait. + * Trait DraggableviewsTrait. * * Draggable Views-related steps. * * @note This is currently limited to nodes only. */ -trait DraggableViewsTrait { +trait DraggableviewsTrait { /** * Save order of the Draggable Order items. diff --git a/src/MetaTagTrait.php b/src/MetatagTrait.php similarity index 97% rename from src/MetaTagTrait.php rename to src/MetatagTrait.php index cacbea2..908ff24 100644 --- a/src/MetaTagTrait.php +++ b/src/MetatagTrait.php @@ -7,13 +7,13 @@ use Behat\Gherkin\Node\TableNode; /** - * Trait Metatag. + * Trait MetatagTrait. * * Steps to work with Metatag. * * @package DrevOps\BehatSteps */ -trait MetaTagTrait { +trait MetatagTrait { /** * Assert that a meta tag with specific attributes and values exists. diff --git a/tests/behat/bootstrap/FeatureContext.php b/tests/behat/bootstrap/FeatureContext.php index 2577c98..a88f844 100644 --- a/tests/behat/bootstrap/FeatureContext.php +++ b/tests/behat/bootstrap/FeatureContext.php @@ -11,7 +11,7 @@ use DrevOps\BehatSteps\ContentTrait; use DrevOps\BehatSteps\CookieTrait; use DrevOps\BehatSteps\DateTrait; -use DrevOps\BehatSteps\DraggableViewsTrait; +use DrevOps\BehatSteps\DraggableviewsTrait; use DrevOps\BehatSteps\EckTrait; use DrevOps\BehatSteps\ElementTrait; use DrevOps\BehatSteps\EmailTrait; @@ -23,7 +23,7 @@ use DrevOps\BehatSteps\LinkTrait; use DrevOps\BehatSteps\MediaTrait; use DrevOps\BehatSteps\MenuTrait; -use DrevOps\BehatSteps\MetaTagTrait; +use DrevOps\BehatSteps\MetatagTrait; use DrevOps\BehatSteps\OverrideTrait; use DrevOps\BehatSteps\ParagraphsTrait; use DrevOps\BehatSteps\PathTrait; @@ -46,11 +46,11 @@ class FeatureContext extends DrupalContext { use BigPipeTrait; - use CookieTrait; use ContentTrait; + use CookieTrait; use EckTrait; use DateTrait; - use DraggableViewsTrait; + use DraggableviewsTrait; use EmailTrait; use ElementTrait; use FieldTrait; @@ -60,7 +60,7 @@ class FeatureContext extends DrupalContext { use KeyboardTrait; use LinkTrait; use MediaTrait; - use MetaTagTrait; + use MetatagTrait; use MenuTrait; use OverrideTrait; use ParagraphsTrait; diff --git a/tests/behat/features/draggableviews.feature b/tests/behat/features/draggableviews.feature index 68498d2..35bc820 100644 --- a/tests/behat/features/draggableviews.feature +++ b/tests/behat/features/draggableviews.feature @@ -1,5 +1,5 @@ @api -Feature: Check that DraggableViewsTrait works +Feature: Check that DraggableviewsTrait works Scenario: Assert save order of the Draggable Order items Given "draggableviews_demo" content: @@ -29,7 +29,7 @@ Feature: Check that DraggableViewsTrait works Then the ".view-draggableviews-demo .views-row:first-child .views-field-title" element should contain "Test 2" And the ".view-draggableviews-demo .views-row:nth-child(2) .views-field-title" element should contain "Test 1" - @trait:DraggableViewsTrait + @trait:DraggableviewsTrait Scenario: Assert that negative assertion for "When I save the draggable views items of the view :view_id and the display :views_display_id for the :bundle content in the following order:" step throws an exception Given some behat configuration And scenario steps: diff --git a/tests/behat/features/metatag.feature b/tests/behat/features/metatag.feature index 488878a..ba3b6db 100644 --- a/tests/behat/features/metatag.feature +++ b/tests/behat/features/metatag.feature @@ -1,4 +1,4 @@ -Feature: Check that MetaTagTrait works +Feature: Check that MetatagTrait works @api Scenario: Assert that "Then the meta tag should exist with the following attributes:" step works as expected @@ -7,7 +7,7 @@ Feature: Check that MetaTagTrait works | name | MobileOptimized | | content | width | - @trait:MetaTagTrait + @trait:MetatagTrait Scenario: Assert that negative assertion for "Then the meta tag should exist with the following attributes:" fails with an error Given some behat configuration And scenario steps: @@ -30,7 +30,7 @@ Feature: Check that MetaTagTrait works | name | Non_Existing | | content | width | - @trait:MetaTagTrait + @trait:MetatagTrait Scenario: Assert that negative assertion for "Then the meta tag should not exist with the following attributes:" fails with an error Given some behat configuration And scenario steps: diff --git a/tests/behat/features/search.feature b/tests/behat/features/search_api.feature similarity index 100% rename from tests/behat/features/search.feature rename to tests/behat/features/search_api.feature