Welcome to the Concordia 2 documentation! This page will help you to get started fast. If you run into problems, you can find help on our Slack channel.
Concordia Compiler requires only version 12.20 (or above) and works on Linux, MacOS and Windows.
In this Quick Start, we'll create a simple specification of an interaction with a search page and use . Therefore, download it before continuing.
Step 0: Initialize
If you are starting a new project or whether your project does not have a file named package.json, then run:
You can use NPM, Yarn or PNPM.
Step 1: Install
Install Concordia Compiler :
It will install version 2 that is in alpha stage, although stable.
Note that the needed package name is concordialang (with lang). After installing it, we'll always use npx concordia to execute it.
Step 2: Configure
Concordia Compiler will ask you about some preferences. Press Enter to answer every question with their default values. It will create a configuration file named .concordiarc and install the selected and .
Step 3: Create a feature
Create the folderfeatures :
Now create the file search.feature withinfeatures, with the following content:
About the file:
Feature and Scenario are high-level, business-focused descriptions about the problem to solve. Their sentences are not used to generate test cases. The above example does not describe them.
A Variant describes the expected interaction with the application's user interface (UI) in order to perform a Scenario. Thus, a Variant uses technological vocabulary.
In Concordia, all the interactions with the UI use first person singular ("I"). That "I" represents the actor that is interacting with the application (in the example above, a
Step 4: Execute
Finally, run
✨That's it.Congratulations!✨
It will:
set the testing environment up (once);
generate a test case file and transformed it into a test script file;
execute the test script file; and
Your browser should open automatically during this process and the console will report the execution results.
Your directory should now look like this:
The directory node_modules contains installed dependencies, like tools and frameworks, whose versions are managed with package.json and package-lock.json. The file codecept.json has a basic configuration to run CodeceptJS with Google Chrome and you can to fit your needs.
The file features/search.testcase should look like this:
The Test Case above was produced from the Variant declared in features/search.feature. Some notes about it:
The import clause (line 5) imports the declared file's content.
The tag @generated (line 7) indicates that the Test Case was produced automatically.
The tag @scenario(1)
The file test/search.js contains the test script produced from features/search.testcase using the selected plug-in. It also contains line comments with references to their corresponding lines and columns in the .testcase file:
Concordia Compiler can also generate and for you. All the test cases and test scripts receive line comments that detail the and reference declarations used to produce oracles and test data.
Notes on installation options
You can opt to install Concordia Compiler locally (per project) or globally.
Local installation (recommended)
does not require administrative privileges (i.e., using sudo on Linux or MacOS) to install;
allows every project to have its own, independent installation;
demands using before every command.
Follow the for a install installation.
is included in NodeJS 8.2.0 or above.
Global installation
requires administrative privilegies (i.e., using sudo on Linux or MacOS) to install;
lets you execute the compiler direcly from any folder;
needs more attention when upgrading, especially for plug-ins.
Example:
Note: On Windows, you must omit sudo.
Additional tips:
.
Whether Windows requires administrative privileges to install, right click Windows (Start) menu, click "Command Prompt (Admin)" or alternatively "Windows PowerShell (Admin)", and then proceed with the installation command.
To upgrade Concordia later, change the installation command from install to upgrade
visitor
).
report the test script results.
(line 8) indicates that the Test Case belongs to the first Scenario (1).
The tag @variant(1) (line 9) indicates that the Test Case belongs to the first Variant (1) of the Scenario declared previously.
The Test Case (line 10) is named using the Variant's name plus dash (-) and some (incremental) number. Its content is basically the same as the Variant's, since we did not use any other declarations.
Feature: Search
Scenario: Shows results that correspond to the term
Variant: Search by pressing Enter
Given that I am on "https://google.com"
When I type "concordialang" in <q>
And I press "Enter"
And I wait for 2 seconds
Then I see "npm"
# Generated with ❤ by Concordia
#
# THIS IS A GENERATED FILE - MODIFICATIONS CAN BE LOST !
import "search.feature"
@generated
@scenario(1)
@variant(1)
Test Case: Search by pressing Enter - 1
Given that I am on "https://google.com"
When I type "concordialang.org" in <q>
And I press "Enter"
And I wait for 2 seconds
Then I see "npm"
// Generated with ❤ by Concordia
// source: search.testcase
//
// THIS IS A GENERATED FILE - MODIFICATIONS CAN BE LOST !
Feature("Search");
Scenario("Shows results that correspond to the term | Search by pressing Enter - 1", (I) => {
I.amOnPage("https://google.com"); // (11,2)
I.fillField("q", "concordialang.org"); // (12,2)
I.pressKey("Enter"); // (13,4)
I.wait(2); // (14,4)
I.see("npm"); // (15,2)
});
A Featuredefines a desired behavior for the software, a piece of functionality that gives some business value. A Scenario is a high-level usage scenario of a Feature. A Feature can have many Scenarios.
Since Feature and Scenario are business-oriented declarations, they are not used by Concordia Compiler to generate test cases. You can write them freely. Although, we recommend you to use the template for a Feature and the template for a Scenario. Example:
This is the template:
As a <role>
I would like to <goal to perform>
In order to <received benefit> (or So that <received benefit>
And this is the (GWT) template:
Given <some context or precondition>
When <some action is carried out>
Then <a result or postcondition is observed>
In the GWT template, additional sentences of each kind are written in the next line and receive the prefix and.
Features and Scenarios must have a name, but their description are not required. Thus, you can write the above example like this (although we recommend against):
Feature files receive the extension .feature. Only one feature per file is allowed.
Variants
A Variant is a test-oriented declaration that describes a possible interaction between a user and the system in order to accomplish a Scenario. You can write more than one Variantfor a same Scenario .
A Variant must use the GWT template and first person singular ("I") in its sentences. Concordia Compiler uses techniques to understand Variant sentences, aiming to transform them into test cases. Example:
Concordia Compiler understands the sentences from line 7 to11 as being variations of the same , fill.
Of course, the vocabulary is limited. Although, you can extend it whether you need (by adding new synonyms to a specific dictionary file). Actions and common variations and suggestions are always .
In the sentences above, the values were denoted between quotation marks, e.g."bob@example.com". Numeric values are also accepted and do not need quotation marks. Example: 97.
Widgets (of the user interface) are written between < and >. Example: <#email>. Concordia adopts the well-known to locate widgets, regardless the user interface (its plugins convert them to the format adopted by the testing framework). You can also use (although some plug-ins may not accept it). Examples:
# (hashtag) to find a widget by its id. Example: #email
. (dot) to find a widget by its class, if applicable. Example: .colorful
Whether you are testing a web application, you may find useful. It's a browser extension - available for Google Chrome and Mozilla Firefox - that extends Katalon Recorder to save a recorded interaction as Variant sentences. Using it may reduce the time needed to locate the widgets of an existing application.
States
A State is denoted by a text between tilde (~), such as ~a state~, and must be written inside sentences only. Example:
A can both produce or require states. A State is produced by a Then sentence, like in the prior example. A State is required by a Given or a When sentence. For instance:
By declaring a required State, you're establishing a dependency among the current and the ones that can produce it. Since that State is often produced by a Variant from another Feature, you have to the corresponding Feature file. Concordia Compiler will only search for states from imported Feature files.
When transforming Variants into , Concordia Compiler will:
Remove any (Then) sentences with a produced State; and
Replace any (Given or When) sentences with a required State by sentences from the Variant that can produce it.
Whether there are different s that can produce a (same) required State, Concordia Compiler will be able to combine each of them with the current Variant, aiming to produce that explore different execution paths. For more information, please read the section .
Example
Let's say that we have a simple Login feature like this:
Now let's suppose that a user has to be logged in to access its account details:
Concordia Compiler will produce my-account.testcase with a like the following, that includes steps from both the features:
Concordia Compiler can optimize the in order to reduce the amount of produced test cases and, therefore, the total execution time. You can also opt to get full coverage - which is ideal before releasing a feature.
Test Cases
A Test Case represents a test case produced for a certain Variant . Concordia Compiler can generate many Test Case declarations from a same Variant, each one covering different states, input combinations and oracles. The generation considers the following declarations:
Variant
states
UI Element
The simplest Test Case is one whose sentences are exactly equal to the Variant's. That's the case when the Variant does not use other declarations and it explicits all the input test data to use.
For comparison, whether we simply remove with "Bob"from the sentenceWhen I fill <#name> with "Bob" , Concordia Compiler will generate a pseudo-random value to complete the sentence, and it will be like this: When I fill <#name> with "A~!39esljdns so2u1 *ns%".
Concordia Compiler uses a single seed to generate pseudo-random numbers in its algorithms and values. Everytime it runs, a new seed is produced. You can set the seed using the CLI parameter --seed. By setting the seed, the compiler will pick the same paths and produce the same values - which is desirable for reproducing the same behavior in the application under test, but not for discovering new defects.
Don't worry about writing Test Cases. Write Variants and let Concordia generates the Test Cases for you.
UI Elements
A UI Element represents a user interface element and can denote its expected behavior through properties.
Although a Widget is easy to declare, it can have some shortcomings:
1) It can make a sentence hard to read, specially when it uses XPath or CSS locators.
For example, the sentence
does inform what the action really means. The same sentence with a UI Element reference becomes
Then the UI Element can be declared as:
Here locator is a property that denotes how to find it in the UI.
2. It can be hard to maintain. When a Widget appears in more than one Scenario and its locator needs to change (e.g. you have changed it in the UI), you need to search and replace all its occurrences in the document. With a UI Element, you just need to change the property locator.
3. It does not offer resources for test generation other than its locator.
A UI Element offers properties that can describe the behavior related to it. These properties are used to infer the test data and test oracles to generate.
Example:
Concordia Compiler infers 6 possible from the above declaration, applying different .
These are the UI Element properties:
id or locator ⇨ how to locate the widget
type ⇨ widget type, like textbox, button, etc.
By default, declaring a UI Element without properties is the same as declaring it with:
id or locator equal to the UI Element name in camelCase (e.g., "Net Price" becomes "netPrice"). The adopted case is configurable.
type equal to textbox
Concordia Compiler is able to guess some properties from the others. For instance, editable is enabled when type is a widget capable of receiving input data; data type can be inferred from any attributed value; etc.
Another example:
Concordia Compiler infers 4 possible from the above declaration, applying different .
Please see the language reference for more details. Let's proceed with the other declarations for now.
Import
A Import declaration allows you to import declarations from another .featurefile. Example:
Imported declarations are Feature, Constants, Table, Database, and UI Element.
Constants
A Constants block defines one or more constants. Every Constant holds an immutable value that is accessible through the declared name. Example:
Constants are accessed by their name between [ and ]. Example:
Constants are global declarations and cannot have duplicate names. You can use them in UI Element properties and Variant sentences.
Table
A Table declares values in rows and columns to use in UI Element properties. Example:
Concordia Compiler infers 4 possible from the above declaration, applying different .
Note that is used to select values from the declared table, which is denoted between [ and ] (like Constants). Table names are global and share the same namespace as Constant names.
Database
A Database provides a way to indicate a connection to an existing . Example:
Concordia Compiler infers 4 possible from the above declaration, applying different .
is used to select values from a table or view from the declared database, which is denoted between [ and ] . Its table or view names are separated by a dot (.). Database names are global and share the same namespace as Tables and Constants.
Test Events
In order to set the execution environment up, testers can define events that can run database commands or command-line scripts. These events can occur before or after Scenarios or Features:
Before Each Scenario: executes before every test of each Scenario of the current Feature.
After Each Scenario: executes after every test of each Scenario of the current Feature.
Before Feature
Test Events are written like Variants, using Given-When-Then. Since they usually perform actions, it is very likely to useWhen in most sentences. Example:
Commands and database scripts are denoted between apostrophe (').
)
@ (at) to find a widget by its name. Example: @pass
~ (tilde) to find a widget by its mobile name, if applicable. Example: ~pass
no prefixed characters, to find a widget by its type. Example: input
// (double slash) to find a widget by its XPath. Example: //table/tbody/tr[1]
Constants
Table
Database
editable ⇨ whether the UI Element is editable (or not)
data type ⇨ data type of an editable UI Element
required⇨ whether the UI Element is required (or not)
format⇨ data format, denoted by a regular expression
value⇨ defines how the value is produced
minimum value ⇨ defines how the minimum value is produced
maximum value ⇨ defines how the maximum value is produced
minimum length ⇨ defines how the minimum length is produced
maximum length ⇨ defines how the maximum length is produced
data type equal to string
: executes once, before all the tests of the current Feature.
After Feature: executes once, after all the tests of the current Feature.
Before All: executes once, before all the tests of the application under test.
After All: executes once, after all the tests of the application under test.
Feature: Sales Report
As a manager
I would like to generate a sales report
In order to track company sales
Scenario: Daily sales report
Given that I am authenticated as a manager
When I open the sales report
and I choose the option "Daily report"
Then I see a list with all the today's sales
and the total value
Feature: New User
Scenario: Successful registration
Variant: Register with a new account
Given that I am on "https://example.com/register"
When I fill <#name> with "Bob"
and I fill <#dateOfBirth> with "11/30/1990"
and I enter with "bob@example.com" in <#email>
and I inform "m123456" in <#pass>
and I type "m123456" in <#confirmation>
and I click <#ok>
Then I see "Welcome, Bob"
Then I have a ~user logged in~
Given that I have ~user logged in~
Feature: Login
Scenario: Sucessful login
Variant: Login with username and password
Given that I am on the [Login Screen]
When I enter with "bob" in {Username}
and I enter with "123456" in {Password}
and I click on {OK}
Then I see "Welcome"
and I have ~user logged in~
Constants:
- "Login Screen" is "/page"
UI Element: Username
UI Element: Password
UI Element: OK
- type is button
import "login.feature"
Feature: My Account
Scenario: See my account data
Variant: Show basic data by default
Given that I have ~user logged in~
and I am on the [Account Screen]
Then I see {User} with "bob"
and I see {Name} with "Robert Downey Jr"
Constants:
- "Account Screen" is "/account"
UI Element: User
import "my-account.feature"
@scenario(1)
@variant(1)
Test Case: See my account data - 1
Given that I am on the "/login"
When I enter with "bob" in <username>
and I enter with "123456" in <password>
and I click on <ok>
Then I see "Welcome"
Given that I am on the "/account
Then I see <user> with "bob"
and I see <name> with "Robert Downey Jr"
When I click on <//*[@id="section1"]/div[1]/div[2]>
UI Element: Net Price
- minimum value is 10.00
Otherwise I see "Net Price must be greater than U$ 10"
UI Element: Profession
- value is in [ "Programmer", "Tester", "Analyst" ]
Import "login.feature"
Constants:
- "Register Page" is "https://example.com/register"
- "Coupon Number" is 12345
Given that I am on the [Register Page]
When I fill {Coupon} with [Coupon Number]
Table: Customer
| name | age |
| Ada Lovelace | 36 |
| Dennis Ritchie | 70 |
| Donald Knuth | 82 |
| George Boole | 49 |
| Niklaus Wirth | 83 |
UI Element: Name
- value comes from "SELECT name FROM [Customer]"
Otherwise I see "Customer not found."
Database: AcmeDB
- type is "mysql"
- name is "acme"
- host is "http://127.0.0.1"
- username is "tester"
- password is "testing123"
UI Element: Name
- value comes from "SELECT name FROM [AcmeDB.customer]"
Otherwise I see "Customer not found."
Before Feature:
Given that I connect to [AcmeDB]
When I run the script 'DELETE FROM customer'
and I run the script 'INSERT INTO customer ( name, age ) VALUES ( "Alice", 20 ), ( "Bob", 30 )'
and I run the command 'rmdir log'
It makes the compiler to ignore a piece of text. Example:
Special line comment
Local declaration.
At most one declaration per file.
They are:
#language: <value> defines the document's language, where <value> is an available language such as en (English) or pt (Portuguese).
#locale: <value> defines document's locale, where
Example:
Tag
A tag adds metadata to a declaration.
Local declaration.
Allowed more than one tag per language construction.
A tag starts with @. Multiple tags can be declared in a single line. Examples:
Some tags can receive parameters. Examples:
Special tag
A special tag changes the compiler's behavior. These are special tags:
@scenario( <number> ): references a by its index, starting at 1.
@variant( <number> ): references a by its index, starting at 1.
Reserved for future use:
@global makes a UI Element globally accessible. Global UI Elements must not have the same name. Local UI Elements (the default) overrides global UI Elements.
@extends( <name> )
Common tag
A common tag is ignored by the compiler and aims to provide meaningful metadata for a declaration. Any tag that is not a special tag is considered a common tag. Examples:
@critical may indicate that the current feature is critical for the project;
@issue(20) may indicate that a feature (or scenario or variant) is being implemented in the Issue 20 from the Issue control system.
Feature
Desired behavior of a software system
Global declaration with unique name, at most one declaration per file.
Sentences are optional and are not used for generating test cases.
A Feature can be a piece of functionality or some needed, wished or distinguish characteristic of a system.
Example:
Feature sentences should try to describe the contribution that the Feature brings to the business addressed by the software system. They are optional, but recommended. Example:
In the above example the benefit to the business is to prevent unauthorized access.
Concordia adopts a wide-spread template for describing them: . A User Story template goes like this:
The order of the sentences can vary. A <role> is usually a type of user or stakeholder. A <capability> is what the role wants to perform in order to achieve the <benefit>, which often is the contribution to the business.
State
A certain state of the system
Local declaration, only accepted insidesentences.
Used for generating test cases.
A State is denoted by a text between tilde (~), such as ~a state~, and must be written inside sentences. Example:
A can both produce or require states. A State is produced by a Then sentence, like in the prior example. A State is required by a Given or a When sentence. For instance:
By declaring a required State, you're establishing a dependency among the current and the ones that can produce it. Since that State is often produced by a Variant from another Feature, you have to the corresponding Feature file. Concordia Compiler will only search for states from imported Feature files.
When transforming Variants into , Concordia Compiler will:
Remove any (Then) sentences with a produced State; and
Replace any (Given or When) sentences with a required State by sentences from the Variant that can produce it.
Whether there are different s that can produce a (same) required State, Concordia Compiler will be able to combine each of them with the current Variant, aiming to produce that explore different execution paths. For more information, please read the section .
Let's say that we have a simple Login feature like this:
Now let's suppose that a user has to be logged in to access its account details:
Concordia Compiler will produce my-account.testcase with a like the following, that includes sentences from both the features:
Scenario
High-level, business-focused, usage scenario of a Feature.
Local declaration with a unique name.
One or more declarations per Feature.
Example:
Template:
Variant
Test-oriented declaration that describes a possible interaction between a user and the system in order to accomplish a Scenario.
Local declaration, one or more per Scenario.
Sentences are used for generating test cases.
A Variant must use the Given-When-Then template (see ) and first person singular ("I") in its sentences. Concordia Compiler uses techniques to understand them.
See the in Variant sentences.
Example:
Things that can be part of Variant sentences:
References to , such as [New Account Screen]
References to , such as {Name} or {New User:Name}.
Best practices
Make sure that your preconditions are part of the group with Given sentences.
Make sure that your actions are part of the group with When sentences.
Widgets
Variant sentences can have widgets(of the user interface) denoted between < and >. Example:
Concordia adopts the well-known to locate widgets. Plugins convert these selectors to the format adopted by the testing framework.
Available locators:
Whether you are testing a web application, you may find useful. It's a browser extension - available for Google Chrome and Mozilla Firefox - that extends Katalon Recorder to save a recorded interaction as Variant sentences. Using it may reduce the time needed to locate the widgets of an existing application.
Constants
Declaration block with constant values
Global declaration (can be used by other files).
At most one declaration per file.
A Constants block can define one or more constants, preceded by a dash sign (-). Both constant names and values are declared between quotes, except for numeric values - that can be declared without quotes. Example:
Constant values are accessible by their names between [ and ]. Examples for the constants above:
Table
Defines a data table that can be used by UI Elements' properties.
Global declaration (can be used by other files).
Zero, one or many declarations per file.
A table must have a unique name. Their rows are denoted between pipe (| ). The first row must provide the column names. Other rows must provide the corresponding data. Example:
Table declarations are transformed into in-memory tables that can be queried by UI Element properties for selecting test data. Tables can be queried using SQL. Like , their names must be referenced between [ and ]. Example:
Database
Declares a database connection that can be used for test data selection
Global declaration.
Names are shared with Constants and Tables.
Example 1:
Example 2:
Database support needs installation of the corresponding drives. See for more information on how to install them.
Properties:
Property
Description
Required
UI Element
Import
Imports declarations from a .feature file
Local declaration.
Give access to the declarations from the imported file.
Examples:
Test Case
Test Events
Using Databases
Concordia can use databases to retrieve test data or to set the testing environment up before or after running test scripts.
Installation
Installing
To reduce the number of downloaded packages, Concordia Compiler 2 does not install database drivers by default anymore - as version 1 did.
Use --db-install plus the driver name. Example:
Available driver names:
Database drivers are installed as development dependencies of your application.
You can also install or uninstall them with NPM, prefixing the driver name with database-js-. Example: npm i -D database-js-mysql.
Uninstalling
Use --db-uninstall plus the driver name. Example:
Upgrading
Just uninstall and then install it again.
Usage
A is global declaration. You must declare it once and then import its file in other features. Example:
A database can be referenced inside UI properties and Test Events by its name between [ and ]. Example: [My DB]. To reference a database table, separate the table name from the database name with with a dot (.) - e.g. [My DB.profession]. Let's see an example:
Instead of reinventing the wheel, Concordia uses SQL to query and modify databases and files. Even JSON and INI files can be queried with SQL.
In the example above, the property value from the UI Element "Profession" indicates that its (valid) values are retrieved from the table "profession" that belongs to the database declared as "My DB". The test event Before Each Scenario was declared to connect to the database, clean the table "profession", and then insert new records that the UI Element "Profession" will use.
See also
Using a Plug-in
Concordia Compiler uses plug-ins for:
setting the testing environment up;
generating and executing test scripts;
converting and reporting test results.
Every plug-in can generate test scripts for a different testing framework or programming language.
Plug-ins
Notes:
(*) RTS means "Requires a ", that is, if it requires a testing server to run the test scripts (e.g., Selenium Standalone).
(**) Appium requires Java for testing web-based mobile applications or using the Android SDK.
codeceptjs-testcafe uses the frameworks and , and works with probably any browser.
Installation
Use --plugin-install plus the plug-in name. Example:
You can also use NPM for installing a plug-in. In this case, you must prefix the plug-in name withconcordialang- . Example: npm install -D concordialang-codeceptjs-testcafe.
Upgrade
Just uninstall the plug-in and then install it again. Example:
How to use a plug-in
Concordia has the following plug-in commands:
plugin-install to install a plug-in
plugin-uninstallto uninstall a plug-in
plugin-serve to start a testing server using the plug-in
All but the latter command (plugin-list) require a plug-in name.
👉 Whether you have a with the property plugin defined, you can omit the plugin name from a command. Example:
Commonly used commands
You can omit the argument <plugin> if you have a configuration file with the property "plugin" defined.
Starting a testing server
Some plug-ins (WebDriverIO, Appium) require a testing server to execute test scripts. A testing server controls a browser or an emulator during tests.
We recommend to open a new terminal/console and then start the testing server:
Naturally, you must replace <plugin> with the plugin name.
The testing server will remain open. To stop it later, type Ctrl + C.
Generating and executing test scripts
Whether your plug-in needs a testing server, start it beforehand.
Generating test scripts without executing them
You can use --no-run to avoid running test scripts and use --no-result to avoid reading the last report with test results. Test scripts will be generated but not executed.
Executing existing test scripts (without generating them)
You can use --no-script to avoid generating test scripts. Only existing test scripts will be executed.
Database: My DB
- type is "mysql"
- host is "127.0.0.1"
- name is "acme"
- username is "tester"
- password is "secret"
Database: Another DB
- type is "json"
- path is "./db.json"
import "db.feature"
# ... other declarations here ...
UI Element: Profession
- value comes from "SELECT name FROM [My DB.profession]"
Otherwise, I see "Profession not found."
- required
Otherwise, I see "Please select the profession."
Before Each Scenario:
Given that I connect to [My DB]
When I run the script 'DELETE FROM profession'
and I run the script 'INSERT INTO profession (name) VALUES ("Enginner"), ("Accountant")'
Take precedence over a CLI option or configuration file option.
<value>
is an available local such as
en-US
(USA English) or
pt-BR
(Brazilian Portuguese).
NOT AVAILABLE YET.
@importance( <number> )
: indicates the importance of a declaration. The importance is as high as its number.
@generated: indicates that a Test Case was computer-generated.
@ignore: whether applied to a Variant, the Variant will not produce Test Cases; whether applied to a Test Case it will not produce test scripts.
@generate-only-valid-values: avoids that a UI Element's property be used for generating values considered invalid. This is specially useful for using with masked input fields, in which the system does not let a user to type invalid characters on it. For instance:
UI Element: Year
- data type is integer
@generate-only-valid-values
- format is "/^[0-9]{1-3}$/"
Otherwise I must see "Year must be a number."
The above example will avoid generating invalid input values (e.g., "A") for testing the format of the Year.
Business-oriented sentences, written as a User Story.
Sentences are optional and are not used for generating test cases.
Business-oriented sentences that follow the template Given-When-Then.
Test-oriented sentences that follow the template Given-When-Then and first-person singular ("I").
Property values must be declared between quotes (").
port
Network communication port used to connect to the database. Whether not defined, the default database port will be used, according to the property type.
No
name
Database name. Used when there is a database server (whose location is defined by the property host) and the database is accessible by its name.
Vary
path
Database path. Used when the database is accessed through the file system, such as the types csv, ini, json, sqlite and xlsx.
Vary
username
Username used to connect to the database. When not defined, the database's default user will be used, according to the property type.
No
password
Password used to connect to the database. When not defined, the database's default password will be used, according to the property type.
No
options
Database-specific connection options.
No
# This is a comment
Feature: Pay with Credit Card # This is also a comment
#language: pt
Funcionalidade: Pagar com Cartão de Crédito
...
@critical @slow
Feature: Print Coupon
...
@gui
Variant: Print to PDF
...
@device
Variant: Print to Plotter
@importance( 6 )
Feature: Register Employee
...
@extends( Date )
UI Element: Birth Date
...
Feature: Administrator Login
Feature: Login
As a registered user
I would like to access to the system using my credentials
So that the system prevents unauthorized access
As a <role>
I would like to <capability>
So that <benefit>
Then I have a ~user logged in~
Given that I have ~user logged in~
Feature: Login
Scenario: Sucessful login
Variant: Login with username and password
Given that I am on the [Login Screen]
When I enter with "bob" in {Username}
and I enter with "123456" in {Password}
and I click on {OK}
Then I see "Welcome"
and I have ~user logged in~
Constants:
- "Login Screen" is "/login"
UI Element: Username
UI Element: Password
UI Element: OK
- type is button
import "login.feature"
Feature: My Account
Scenario: See my account data
Variant: Show basic data by default
Given that I have ~user logged in~
and I am on the [Account Screen]
Then I see {User} with "bob"
and I see {Name} with "Robert Downey Jr"
Constants:
- "Account Screen" is "/account"
UI Element: User
import "my-account.feature"
@scenario(1)
@variant(1)
Test Case: See my account data - 1
Given that I am on the "/login"
When I enter with "bob" in <username>
and I enter with "123456" in <password>
and I click on <ok>
Then I see "Welcome"
Given that I am on the "/account
Then I see <user> with "bob"
and I see <name> with "Robert Downey Jr"
Feature: Sales Report
Scenario: Daily sales report
Given that I am authenticated as a manager
When I open the sales report
and I choose the option "Daily report"
Then I see a list with all the today's sales
and the total value
Given <some context or precondition>
and <other context or precondition>
and <other context or precondition>
...
When <some action is carried out>
and <other action is carried out>
and <other action is carried out>
...
Then <a result or postcondition is observed>
and <another result or postcondition is observed>
and <another result or postcondition is observed>
...
Feature: New User
Scenario: Successful registration
Variant: Register with a new account
Given that I am on the [New Account Screen]
When I fill {Name}
and I fill {Date of Birth}
and I enter with "bob@example.com" in <#email>
and I inform 123 in <#pass>
and I type 123 in <#confirmation>
and I click <#ok>
Then I have a ~registered user~
and I see "Welcome, Bob"
...
When I fill <#firstName> with "Alice"
Locator
Purpose
Example
#
To find a widget by its id.
When I click on <#save>
.
To find a widget by its class.
Constants:
- "Home" is "/home"
- "App Name" is "Acme"
- "Mininum Age" is 18
- "Pi" is 3.1416
Variant: Example
Given that I visit [Home]
and I see [App Name]
# ...
UI Element: Age
- minimum value is [Minimum Age]
Otherwise I see "Invalid age."
UI Element: Some Math Value
- maximum value is [Pi]
UI Element: Profession
- value comes from "SELECT name FROM [professions]"
UI Element: Salary
- minimum value comes from "SELECT min_salary from [professions] WHERE name = {Profession}"
Database: My DB
- type is "mysql"
- host is "http://127.0.0.1"
- name is "mydb"
- username is "admin"
- password is "p4sss"
Database: Another DB
- type is "json"
- path is "C:\path\to\db.json"
type
Database type. Examples: "adodb", "csv", "firebase", "ini", "json", "mysql", "mssql", "postgres", "sqlite", "xlsx". See Using Databases for all supported types.
This example covers the generation of test data and oracles and the combination of features using states.
Let's suppose that we have to create two simple features for a web application: "Login" and "Add Product". After discussing them with stakeholders and the team, we created the following Concordia specification (see the two tabs):
Feature:LoginAsauser
import "login.feature"
Feature: Add Product
As an employee
I would like to add a product
In order to manage its data and sale it
Scenario: Add a new product with basic data
Given that I am logged in
When I add a product with SKU, description, department, price, and quantity
Then I am able to register it
Variant: Basic data
Given that I have a ~user logged in~
and I visit the [Product Screen]
When I fill {SKU}
and I fill {Description}
and I fill {Price}
and I fill {Quantity}
and I click on {Save}
Then I have ~product registered~
and I see "Saved."
UI Element: SKU
- required
Otherwise I see "Please inform the SKU."
- format is "/[A-Z]{3}\-[0-9]{3}/"
Otherwise I see "Invalid SKU format. Please use 3 letters, dash, 3 numbers."
UI Element: Description
- minimum length is 2
Otherwise I see "Description needs at least 2 characters."
- maximum length is 100
Otherwise I see "Description must have at most 100 characters."
UI Element: Price
- required
Otherwise I see "Please inform the price."
- minimum value is 0.01
Otherwise I see "Minimum price is one cent."
UI Element: Quantity
- data type is integer
UI Element: Save
- type is button
Constants:
"Product Screen" is "/products/new"
These features could correspond to the following sketches:
There is an that can generate User Interface Prototypes (UIP) from a Concordia specification. The current version has a plug-in to create HTML-based UIPs and it's really easy to adapt it for other technologies.
The specification helps stakeholders, analysis, testers and developers to create a shared understanding about the application and its business rules. It helps with the requirements validation, knowledge documentation, project design, implementation and testing.
Different from Use Cases and other requirements specification formats, with Concordia you probably don't need to define different scenarios for validation. Instead of having to manually define a set scenarios for validation, you just need to define how the UI elements should behavior - in their declaration - and the compiler will generate different scenarios for you in the form of Test Cases. Whether your application has a lot of such rules, your team will certainly benefit from it.
<TO-DO (UNDER CONSTRUCTION)>
that a Variant express a possible interaction between a user and the applicationin order to complete its (business-focused) Scenario. lt follows the syntax and always uses the word "I" to represent the user or user role denoted in the Feature's description.
Let's run Concordia Compiler with a to produce the same results over and over again:
It will generate login.testcase with the following content:
If a seed is not given, Concordia Compiler assumes the current date and time. The seed is always printed in the console so that you or your team can reproduce the same paths, test data, and test oracle that were able to expose a bug in your application. Here we are giving it for a sake of reproduction.
Using the same seed over and over again will make Concordia Compiler produce the same results. Options no-run and no-result avoid running the test scripts and getting execution results.
Execution without generation
This will not generate test cases or test scripts, but it will execute them and get their results:
Output
login.testcase:
test/login.js:
3. Analyze the results
Whether you ran the test scripts above, you probably saw they fail. That's because they didn't find a web application running at http://localhost/login or because the application was found but it did not match the expected behavior. You may adapt your application and run the tests again.
Concordia shows a report that indicates the failures' locations. They help you to decide if a failure was caused by the application under test (e.g., it did not behave as expected) or because of the requirements specification (e.g., it is outdated in relation to the application).
Now keeping your specification updated has a new clear benefit: you can use it to generate tests and discover existing defects in your application!
See also
Understanding Concordia
Imagine a solution in which you can write software requirements using an Agile-style, restricted natural language and it automatically generates for you using effective testing techniques such as , , and and it can also use test data from . Well, that's Concordia. Its solution is composed by a metalanguage and a compiler.
Concordia is also who was the personification of "concord" or "agreement". The idea is that the metalanguage help users, stakeholders, and software teams to discussing and reaching an agreement about the software requirements. A shared understanding about the requirements is essential to create the right software.
Overview
To cite Concordia in a paper, please cite its PhD Thesis:
PINTO, Thiago Delgado. Unifying Agile requirements specification quality control and implementation conformance assurance. 2018. Doctoral Thesis. Pontifical Catholic University of Rio de Janeiro, Informatics Department, 2018.
Concordia Compiler infers test cases, test data and test oracles from specification files written in Concordia and tries to cover a large number of paths that can expose defects in your application. Its approach keeps the specification relevant, up-to-date and more useful for stakeholders, testers, and developers than other documentation formats.
Command Line Interface
CLI
Basic syntax is:
where:
A parameter between[ and ] means that it is optional.
Concordia metalanguage allows you to focus on writing your application's business rules and defining its expected behavior for when users interact with it. Stakeholders, business analysts, testers, and programmers can now discuss the application using a single language, a single source of truth. Business-focused and testing-focused declarations are clearly separated, so you can discuss technological details only with the interested parties.
With Concordia, you can write both functional and non-functional requirements, although it produces only functional test cases. Because it adopts Agile-style declarations, you don't have to write it so formally (like Use Cases usually do) and you can use plain text - which is easy to evolve with your codebase.
We invite you to take a look at the language and see how it is easy to understand. It is currently available in English (en) and Portuguese (pt).
Concordia compiler
Concordia compiler mix compiler techniques with machine learning and natural language processing to understand declarations in Concordia Language - which uses restricted natural language. After detecting declarations, it infers test scenarios, test data, and test oracles to generate test cases in a framework-independent format (see the produced test cases). Finally, it uses a plug-in created for a specific testing framework to generate test scripts (that is, source code), execute them and read their results. You don't need to write code. And the compiler checks your declarations for logic errors and other possible problems.
Concordia compiler uses plug-ins to transform Abstract Test Cases (ATS) into test scripts - that is, into source code. Every plug-in can be implemented to generate source code for a specific programming language and testing framework - in Java, Python, Go, PHP or whatever the language you prefer. The generated source code is not tied to JavaScript or TypeScript.
By default the configuration file is loaded from the current directory.
--init
Creates a configuration file and optionally installs plug-ins and databases.
This command asks about your preferences. By hitting Enter without giving an answer, it will select the suggested (default) value. You can use Up and Down to navigate the answers and hit Enter to select it. For multiple selection (when available), hitSpace.
By using a configuration file (e.g. .concordiarc) you avoid informing the configured parameters in the console/terminal. For instance, whether plugin is defined in the configuration file, you don't need to inform --plugin anymore.
Maximum random string size to generate, when free values are allowed.
The default value is 500 .
--random-min-string-size
Minimum random string size to generate, when free values are allowed.
The default value is 0 .
--random-tries
Random tries to generate invalid values.
When there is a set of valid values and any value different from those in the set is considered an invalid, random generation is used to produce an invalid value. This parameter defines how many tries to take. Usually it gets on the first try.
Concordia Compiler can use random selection in its algorithms. By default, it generates the seed using the current date and time. Using the same seed makes the algorithms produce the same output (e.g., same values and paths). While that's interesting for reproducing a past behavior, we don't recommend it for finding new defects. Using new seeds, Concordia Compiler will select different test data, test oracles, and test paths over time.
You can also set the algorithms to use, in order to avoid random selection. Full combination (therefore full coverage of the requirements) can be achieved and it is recommend for complete checking before a new release. However, it may take some time to execute.
Combination strategies
--comb-data
--comb-invalid
--comb-state
--comb-variant
File formats and extensions
You probably don't need to use them, unless you're facing problems to read files.
--line-breaker
Sets the character(s) used for breaking lines.
Since by default the line breaker is detected automatically, this command may not be useful for you in most cases.
# Generated with ❤ by Concordia
#
# THIS IS A GENERATED FILE - MODIFICATIONS CAN BE LOST !
import "login-en.feature"
@generated
@scenario(1)
@variant(1)
Test case: Successful login with valid credentials - 1
Given that I am in the "http://localhost/login" # [Login Screen]
When i fill <username> with "*RM)O," # invalid: inexistent element
And i fill <password> with "" # valid: last element
And I click on <ok> # {OK}
Then I must see "Invalid username" # from <username>
@generated
@scenario(1)
@variant(1)
Test case: Successful login with valid credentials - 2
Given that I am in the "http://localhost/login" # [Login Screen]
When i fill <username> with "bob" # valid: filled
And i fill <password> with -8655972838932479 # invalid: inexistent element
And I click on <ok> # {OK}
Then I must see "Invalid password" # from <password>
@generated
@scenario(1)
@variant(1)
Test case: Successful login with valid credentials - 3
Given that I am in the "http://localhost/login" # [Login Screen]
When i fill <username> with "alice" # valid: random element
And i fill <password> with "4l1c3pass" # valid: random element
And I click on <ok> # {OK}
Then I see "Welcome"
@generated
@scenario(1)
@variant(1)
Test case: Successful login with valid credentials - 4
Given that I am in the "http://localhost/login" # [Login Screen]
When i fill <username> with "alice" # valid: last element
And i fill <password> with "4l1c3pass" # valid: filled
And I click on <ok> # {OK}
Then I see "Welcome"
@generated
@fail
@scenario(1)
@variant(1)
Test case: Successful login with valid credentials - 5
Given that I am in the "http://localhost/login" # [Login Screen]
When i fill <username> with "" # invalid: not filled
And i fill <password> with "" # invalid: not filled
And I click on <ok> # {OK}
Then I see "Welcome"
@generated
@scenario(1)
@variant(1)
Test case: Successful login with valid credentials - 6
Given that I am in the "http://localhost/login" # [Login Screen]
When i fill <username> with "bob" # valid: first element
And i fill <password> with 123456 # valid: first element
And I click on <ok> # {OK}
Then I see "Welcome"
// Generated with ❤ by Concordia
// source: c:\code\tmp\concordia-test-pt\login-en.testcase
//
// THIS IS A GENERATED FILE - MODIFICATIONS CAN BE LOST !
Feature("Login");
Scenario("Successful login | Successful login with valid credentials - 1", (I) => {
I.amOnPage("http://localhost/login"); // (61,5) [Login Screen]
I.fillField("username", "*RMO,"); // (11,5) invalid: inexistent element
I.fillField("password", ""); // (12,7) valid: last element
I.click("ok"); // (64,7) {OK}
I.see("Invalid username"); // (14,5) from <username>
});
Scenario("Successful login | Successful login with valid credentials - 2", (I) => {
I.amOnPage("http://localhost/login"); // (61,5) [Login Screen]
I.fillField("username", "bob"); // (21,5) valid: filled
I.fillField("password", "-8655972838932479"); // (22,7) invalid: inexistent element
I.click("ok"); // (64,7) {OK}
I.see("Invalid password"); // (24,5) from <password>
});
Scenario("Successful login | Successful login with valid credentials - 3", (I) => {
I.amOnPage("http://localhost/login"); // (61,5) [Login Screen]
I.fillField("username", "alice"); // (31,5) valid: random element
I.fillField("password", "4l1c3pass"); // (32,7) valid: random element
I.click("ok"); // (64,7) {OK}
I.see("Welcome"); // (65,5)
});
Scenario("Successful login | Successful login with valid credentials - 4", (I) => {
I.amOnPage("http://localhost/login"); // (61,5) [Login Screen]
I.fillField("username", "alice"); // (41,5) valid: last element
I.fillField("password", "4l1c3pass"); // (42,7) valid: filled
I.click("ok"); // (64,7) {OK}
I.see("Welcome"); // (65,5)
});
Scenario("Successful login | Successful login with valid credentials - 5", (I) => {
I.amOnPage("http://localhost/login"); // (61,5) [Login Screen]
I.fillField("username", ""); // (52,5) invalid: not filled
I.fillField("password", ""); // (53,7) invalid: not filled
I.click("ok"); // (64,7) {OK}
I.see("Welcome"); // (65,5)
});
Scenario("Successful login | Successful login with valid credentials - 6", (I) => {
I.amOnPage("http://localhost/login"); // (61,5) [Login Screen]
I.fillField("username", "bob"); // (62,5) valid: first element
I.fillField("password", "123456"); // (63,7) valid: first element
I.click("ok"); // (64,7) {OK}
I.see("Welcome"); // (65,5)
});
Write or update your requirements specification with the Concordia Language and validate them with users or stakeholders;
Use Concordia Compiler for generating functional tests from the specification and running them;
If the tests failed, there are some possibilities:
a) You still haven't implemented the corresponding behavior in your application. In this case, just implement it and run the tests again.
b) Your application is behaving differently from the specification. In this case, it may have bugs or you or your team haven't implemented the behavior exactly like described in the specification.
Whether the application has a bug, we are happy to have discovered it! Just fix it and run the tests again to make sure that the bug is gone.
Otherwise, you can decide between changing your application to behave exactly like the specification describes, or changing the specification to match your application behavior. In the latter case, back to step 1.
If the tests passed, great job! Now you can write new requirements or add more test cases, so just back to step 1.
Process
When you type concordia, the compiler performs the following process:
Process
It reads your .feature and .testcase files, and uses a lexer and a parser to identify and check documents' structure.
It uses Natural Language Processing (NLP) to identify sentences' intent, which increases the chances of recognizing different writing styles.
It performs to check recognized declarations.
It uses the specification to infer the most suitable test cases, test data, and test oracles, and then generates .testcase files in Concordia Language.
It transforms all the test cases into test scripts (that is, source code) using a plug-in.
It executes the test scripts with the plug-in. These test scripts will check your application's behavior through its user interface.
It reads and presents execution results. These results relate failing tests to the specification, in order to help you understanding the possible reasons of a failure.
From 0.x to 1.x
Concordia Compiler
It changed the way that all the plug-in operations are handled. See Issue #34 for details.
The behavior of the following plug-in commands were affected:
COMMAND
NOW
BEFORE
plugin-list
It lists all the plug-ins installed for the application.
It listed all the plug-ins available in the compiler.
External tools used by plug-ins are now installed locally, per project, instead of installed globally. Their direct execution (without using Concordia) is now possible through NPX. For instance, the command codeceptjs run test.js must now be executed as npx codeceptjs run test.js. This change also allows the installation of different versions of external tools, when needed.
FAQ
Were there any changes in commands' syntaxes?
No, there were not.
Is my configuration file still compatible?
It depends. All the properties are still compatible, except for "plugin". Whether your configuration file has that property, you should update it.
Is it possible now to install or uninstall a plug-in with NPM ?
Katalon-Concordia is a browser extension for Chrome and Firefox that converts recordings from Katalon Recorder (which is a capture-replay tool - that is, it records all the actions that you perform while interacting with the application under test in order the reproduce them later) to Variant sentences. That's very handful for discovering elements' identification in web applications (e.g., their id properties or their ) and obtaining Variant sentences with the corresponding interaction.
is a command-line tool that generates User Interface Prototypes (UIP) from Concordia features. It can help discussing features with stakeholders and accelerating their construction. Currently there is a customizable plug-in for HTML-based interfaces.
is a command-line tool that generates an HTML page with an interactive graph that shows features files' relationships.
Publications
PINTO, Thiago Delgado. Unifying Agile requirements specification quality control and implementation conformance assurance. 2018. Doctoral Thesis. Pontifical Catholic University of Rio de Janeiro, Informatics Department, 2018.
PINTO, Thiago Delgado; COSTA, Pablo Veiga; GONÇALVES, Willian Inácio. User Interface Prototype Generation from Agile Requirements Specifications written in Concordia. 2019. WebMedia'19: Proceedings on the 25th Brazilian Symposium on Multimedia and the Web, Rio de Janeiro, 2019. ACM. DOI:
Test Coverage and Techniques
Time is a crucial constraint in software development and software teams often need to focus their test efforts on the most important application paths.
⚠ UNDER CONSTRUCTION ⚠
In software testing, test coverage is some kind of metric that helps you to understand what parts of your application (or code) are exercised by your tests. The number of feasible paths through them grows exponentially with an increase in application size and can even be infinite in the case of applications with unbounded loop iterations. That is a problem called path explosion problem. Concordia Compiler deals with it by providing sets of combination and selection strategies, and trying to achieve full path coverage over time.
Test Coverage
All the Features, Scenarios, and Variants are covered by default.
The CLI parameter --files can filter the .feature files to be considered.
The CLI parameter --ignore
State-based Strategies
In Concordia, you can declare a State in a Variant sentence using a text between tile (~), like this:
There are three types of State:
Precondition: when declared in a Given sentence;
State Call: when declared in a When sentence;
Postcondition: when declared in a
Both Preconditions and State Calls are considered required States. That is, they denote a dependency of a certain State of the system that needs to be executed. A Precondition needs to be executed before the Variant starts, and a State Call needs to be executed during the Variant's execution.
A Postcondition is a produced State, that is, a state of the system produced by a successful execution of a Variant. Therefore, whether something goes wrong during a Variant's execution, it will not produce the declared State.
When the current Variant requires a State, Concordia Compiler will look for imported Features' Variants able to produce it. To generate complete Test Cases for the current Variant, it will:
Select the Variants to combine;
Generate successful test scenarios for the selected Variants;
Select the successful test scenarios to combine;
Steps 1 and 3 can adopt different strategies. Concordia Compiler let's you:
Parameterize how the Variants will be selected, using --comb-variant; and
Parameterize how the successful test scenarios will be combined, using --comb-state.
Variant selection
Available strategies for --comb-variant:
random: Selects a random Variant that produces the required State. That's the default behavior;
first: Selects the first Variant that produces the required State;
fmi
Example:
State combination
Available strategies for --comb-state:
sre: Single random of each - that is, randomly selects a single, successful test scenario of each selected Variant. That's the default behavior;
sow : Shuffled one-wise - that is, shuffles the successful test scenarios than uses combination.
Example:
Full vs random selection
Strategies that use random selection can take different paths every time they are used. Furthermore, they reduce considerably the amount of generated paths - i.e., it avoids "path explosion" - and thus the amount of produced test cases.
Full-selection strategies can be used for increase path coverage. Although, it also increases the needed time to check all the paths, which may be undesirable for frequent tests.
By default, Concordia Compiler uses random selection strategies.
Testing Techniques
Concordia Compiler can infer input test data from Variants, UI Elements, Constants, Tables, and Databases. The more constraints you declare, the more test cases it generates.
Adopted techniques to generate input test data include:
These are well-known, effective black-box testing techniques for discovering relevant defects on applications.
Data Test Cases
We call Data Test Cases those test cases used to generate input test data. They are classified into the following groups: RANGE, LENGTH, FORMAT, SET, REQUIRED, and COMPUTED. The group COMPUTEDis not available on purpose, since a user-defined algorithm to produce test data can have bugs on itself. Thus, one should provide expected input and output values in order to check whether the application is able to correctly compute the output value based on the received input value.
Every group has a set of related data test cases, applied according to the declared constraints and selected algorithms:
Maximum length for random string values
By default, the maximum length for randomly-generated string values is 500. This value is used for reducing the time to run test scripts, since long strings take time to be entered.
You can set maximum length using the CLI parameter --random-max-string-size. Example:
You can also set it in the configuration file (.concordiarc) by adding the property randomMaxStringSize. Example:
Properties vs Data Test Cases
Data Test Cases (DTC) are selected for every declared and its properties. The more properties you declare for a UI Element, the more data you provide for Concordia Compiler to generate DTC.
Example of some evalutations:
When no properties are declared, FILLED and NOT_FILLED are both applied and considered as valid values;
When the property required is declared, NOT_FILLED (empty) is considered as an invalid value;
There is more logic involved for generating these values. ...
Example 1
Let's describe a named Salary :
When no property is defined or only the property data typeis defined,
We defined the property data type as double, since the default data type is string.
Since few restrictions were made, Salary will be tested with the test cases of the group REQUIRED:
FILLED: a pseudo-random double value is generated;
NOT_FILLED: an empty value will be used.
Now let's add a minimum value restriction.
Some tests of the group RANGE are now applicable:
LOWEST_VALUE: the lowest possible double is used
RANDOM_BELOW_MIN_VALUE: a random double before the minimum value is generated
JUST_BELOW_MIN_VALUE
Since 1000.00 is the minimum value, the data produced by the tests 1, 2, 3, and 6 of the group VALUE are considered invalid, while 4 and 5 are not. For these tests considered invalid, the behavior defined in Otherwise, that is
is expected to happen. In other words, this behavior serves as and must occur only when the produced value is invalid.
Unlike this example, when the expected system behavior for invalid values is not specified and a test data is considered invalid, Concordia expects that test should fail. In this case, it generates the Test Case with the tag @fail.
Now let's add maximum value restriction:
All the tests of the group RANGE are now applicable. That is, the following tests will be included:
MEDIAN_VALUE: the median between the minimum and the maximum values
RANDOM_BETWEEN_MIN_MAX_VALUES: a pseudo-random double value between the minimum and the maximum values
JUST_BELOW_MAX_VALUE
The tests from 5 to 7 will produce values considered invalid.
Example 2
Let's define a user interface element named Profession and a table named Professions from which the values come from:
Applicable test are:
FILLED
NOT_FILLED
FIRST_ELEMENT
The first two tests are in the group REQUIRED. Since we declared Profession as having a required value, the test FILLED is considered valid but NOT_FILLED is not. Therefore, it is important to remember declaring required inputs accordingly.
The last four tests are in the group SET. Only the last one, NOT_IN_SET, will produce a value considered invalid.
Example 3
In this example, let's adjust the past two examples to make Salary rules dynamic and change according to the Profession.
Firstly, we add two columns the the Professions table:
Then, we change the rules to retrieve the values from the table:
The reference to the UI Element {Profession} inside the query, makes the rules of Salary depend on Profession. Every time a Profession is selected, the minimum value and the maximum value of Salary changes according to the columns min_salary and max_salary of the table Professions.
Yes, this project does not have a logo yet 😱. Designers interested in helping us (for free 🙏), please contact us on our or .
The logo should reflect the idea behind the language:
Concordia is who was the personification of "concord" or "agreement". The idea is that the metalanguage may help users, stakeholders, and the software team to discuss and to reach an agreement about the software requirements. This shared understanding is essential to the software construction.
The tool generates test cases and test scripts that verify if the application under test meets the specified functional requirements. Therefore, the idea of "verification" or "testing" could be in the logo.
Plug-ins
Interested? See .
👉 Our next goal is a plugin for .
Suggestions:
JavaScript and friends: (web), (web, mobile, desktop)
PHP: (web)
Java: (web), (android), (desktop)
Language
Variant Background
This language construction is part of Concordia Language since the first version, but it was not implemented yet.
A Variant Background defines steps that must be executed before all the Variants of a certain Scenario. Example:
MOTIVATION: It allows to keep initial, repeated steps in a common place.
Constant names without quotation marks
Like this:
MOTIVATION: Simpler syntax.
Visual comparison
Please see .
MOTIVATION: It allows to perform visual comparison and detect related bugs.
File content comparison
To use the characters """ (three quotation marks) to denote a text content to be compared. Example:
MOTIVATION: It facilitates comparisons with text files or multiple-line strings.
Table matching
To allow a given UI Element or UI Literal to match a certain Table.
Example 1:
Example 2:
In which Some Table is declared like this:
It should make target table's rows to match the declared ones.
MOTIVATION: It facilitates the verification of expected data in tables from a user interface.
NOTES: Probably it requires to convert the Concordia Action to many abstract commands that verify every table row independently. Usually testing frameworks do not provide such kind of verification (for many rows at once).
Date and time expressions inside tables
Use Concordia 2 data and time expressions inside table rows. They must be written between ` or some other character. Example:
MOTIVATION: Concordia Language allows date and time expressions inside UI Properties. Using them inside tables can be useful.
Dynamic states
States that vary according to some generated value. Example:
According to the selected user, it will produce a different State. For example, when "bob" is selected, the produced state will be admin is logged in and when "joe" is selected, the produced state will be guest is logged in .
Thus, Features could depend on static or dynamic states.
MOTIVATION: Making states more flexible.
Alternative states
To provide a Given sentence that requires one - and only one - of the provided states. Example:
MOTIVATION: It allows to perform a XOR operation with the given states using natural language, aiming at choosing one and only one of the given states.
Annotations to parameterize combination strategies
To provide annotations parameterize a test combination strategy for a specific Feature.
For instance, to configure the combination of invalid values for a specific Feature:
The annotations should correspond to the CLI parameters.
MOTIVATION: Increase the flexibility of the test generation procedure.
Multi-line queries
Currently:
Proposal (to accept as valid):
Alternative proposal:
MOTIVATION: Make it easier to write/read SQL statements.
Compiler
Selection Options
Select a Feature for Test Case generation, without having to include its dependencies:
by importance value
Select a Scenario for Test Case generation:
Watch Mode
Provide the option --watch to generate .testcase files when their corresponding .feature file changes or an imported .feature file changes. A new seed must be produced, except when explicitly provided (using --seed or configuration file).
Language Support
Concordia has language constructions that the Compiler does not support yet. Examples:
Variant Background: Implement Variant Background, which is part of the Concordia Language but was not implemented yet by the Concordia Compiler.
Support the tag in Features and Scenarios. Currently it is supported inVariants and Test Cases. Whether added to a Feature or a Scenario, it would not generate Test Cases.
New Test Cases
Test cases that explore SQL Injection: Using declared Databases and Queries to formulate Test Cases that generate strings with SQL Injection commands.
Test cases that use naughty strings as test data: Using a as test data.
UI and Report
It is already possible to generate HTML or even PDF reports using the available plug-ins. However, these reports indicate the frameworks' results, not necessarily the ones from Concordia.
To provide the CLI parameter --report for indicating the corresponding plug-in. Example:
which should be equivalent to
Tool integration
Integration with text editors
Create new projects for auto-completion plug-ins for text editors such as VS Code, Sublime Text, Atom, etc. Example for VS Code: .
Be able to run test scripts using Concordia from the IDE.
Integration with reporting tools
Create integration with . Other reporters (e.g., ) can be added further.
Performance improvements
Avoid generating test scripts when their Test Case files did not change
Keep some hash control or use Git information when available. Hashes can be stored in a .json or .yml file. Example:
Generate unrelated files in parallel
NodeJS has made considerable progress since version 10.5 adding support to . Stable support for Worker Threads was added in version 12 LTS. Although, it would increase minimum requirements to install Concordia Compiler - currently it requires NodeJS 8.0.
Although Semantic Versioning is conceived for APIs instead of for applications, we adopt a very similar convention. Thus, changes become predictable and you can know, from the version numbers, when a version is no more compatible with a previous version.
Given a version MAJOR.MINOR.UPDATE:
MAIOR is increased when the Compiler or the Language is no more compatible with the previous version.
MINOR is increased when adding functionality in a backwards-compatible manner.
Examples:
0.2.0 is compatible with 0.1.0
0.1.1 is compatible with 0.1.0
Upgrade
NPM upgrades without breaking compatibility (when MINORorUPDATE changes).
For migrating a MAJOR version (e.g., 1.x to 2.x) please read our .
To upgrade a local installation:
To upgrade a global installation:
Configuration file
Concordia Compiler can use a configuration file in named .concordiarc. You can generate it by running the following command:
You'll be asked about the desired configuration and then the file will be saved.
Example:
Configuration file's properties are similar to CLI parameters. The example above defines: "pt" (meaning "Portuguese") as the default language; the directory "docs/features" for features and test cases; the plug-in "codeceptjs-testcafe"
Migration Guide
Migrations are only needed when upgrading to a major version. See for more information.
To upgrade to a major version:
Uninstall the current version, e.g.: npm uninstall -D concordialang
Creating a Plug-in
Introduction
Concordia Compiler uses plug-ins for generating test scripts, running them and reading their results.
A plug-in can...
Actions vs APIs
This page presents Concordia actions and their corresponding commands for some frameworks.
Help us improving this page. Send us a message on or open an Issue at . 💖
👉 Under construction.
Only the plugin's dependencies were installed, since the plug-in was embedded in the compiler.
plugin-uninstall
It uninstalls a plug-in and its dependencies using npm.
Only the plugin's dependencies were uninstalled, since the plug-in was embedded in the compiler.
plugin-info
It shows information about a plug-in installed for the application.
It shows information about a plug-in available in the compiler.
plugin-serve
It starts a testing server using a plug-in installed for the application.
It started a testing server available in the compiler.
The tag@ignore can be used to mark a Feature or Variant to be ignored by the test generator. However, it can still be used by other Features or Variants.
The tag@importance (e.g., @importance( 8 )) can be used to denote the importance of a Feature. CLI parameters --sel-min-feature and --sel-max-feature can then be used to filter the features to be considered by the test generator. Example: concordia --sel-min-feature 7 makes the compiler considers the features with importance value of 7 or above. By default, all the features receive an importance value of 5.
Variants are selected and combined using State-based Strategies (see below).
All the UI Elements constraints are covered by default, using a set of Testing Techniques (see below).
Then
sentence.
Generate (successful and unsuccessful) test scenarios for the current Variant;
Combine the selected successful test scenarios with the test scenarios of the current Variant;
Transform all the test scenarios into test cases (i.e., valued test scenarios).
: Selects the first most important Variant (since two Variants can have the same importance value) that produces the required State;
all: Selects all the Variants that produce the required State.
Now proceed as described below (depending on your current version).
Note: For a global installation, replace -D with -g.
From 1.x to 2.x
1. Update your plug-in
Concordia 2 has a new plugin API, not compatible with plug-ins for Concordia 1. So uninstall your current plug-in and install a new one. Example:
Alternatively, you can install one of the new plug-ins then change your configuration file to use it. Example:
2. Install database drivers
Whether your Concordia specification access a database, you have to install the corresponding database driver. Concordia 2 does not install them by default anymore, as Concordia 1 did, aiming to reduce the amount of downloaded dependencies.
Concordia 2 has a new CLI option --db-install to help with that. Example:
See Database Drivers to install the proper driver(s) for your application.
From 0.x to 1.x
1. Update your configuration file, if needed
Whether you project has a configuration file .concordiarc, open it with a text editor. If the file has a property "plugin" with the value "codeceptjs", you must change it to "codeceptjs-webdriverio".
2. Install the new plug-in
You can install any of the available plug-ins, currently codeceptjs-webdriverio or codeceptjs-appium. Example:
UI Element: Salary
- data type is double
- minimum value is 1000.00
Otherwise I must see "Salary must be greater than or equal to 1000"
Otherwise I must see "Salary must be greater than or equal to 1000"
UI Element: Salary
- data type is double
- minimum value is 1000.00
Otherwise I must see "Salary must be greater than or equal to 1000"
- maximum value is 30000.00
Otherwise I must see "Salary must be less than or equal to 30000"
UI Element: Profession
- type is select
- value comes from "SELECT name from [Professions]"
- required
Table: Professions
| name |
| Lawyer |
| Accountant |
| Dentist |
| Professor |
| Mechanic |
UI Element: Salary
- data type is double
- minimum value comes from the query "SELECT min_salary FROM [Professions] WHERE name = {Profession}"
Otherwise I must see "The given Salary is less than the minimum value"
- maximum value comes from the query "SELECT max_salary FROM [Professions] WHERE name = {Profession}"
Otherwise I must see "The given Salary is greater than the maximum value"
UI Element: Name
- min length is 2
- max length is 100
@extends( Name )
UI Element: Emergency Contact Name
# Emergency Contact Name inherits the properties from Name
Feature: Search
Scenario: Simple Search
Variant Background:
Given that I visit the [Search Page]
When I fill {Search} with "Hello"
Variant: Search by clicking on Go
When I click on {Go}
Then I see "Hello World!"
Variant: Search by hitting Enter
When I press "Enter"
Then I see "Hello World!"
Constants:
- Net Price is 10.0
- hello is "hello"
...
Then I see the file "out.txt" with
"""
This is some text to be compared.
It will probably
be compared to a
text file.
"""
Then I see the table {MyTable} as [Some Table]
Then I see the table <myTable> as [Some Table]
Table: Some Table
| Name | Age |
| Bob | 21 |
| Suzan | 25 |
Table: Some Table
| Name | Age |
| Bob | `18 years ago` |
...
Given that I fill {User}
and I fill {Pass}
and I click on {OK}
Then I see "Welcome"
and I have ~{User Type} is logged in~
UI Element: User
- value comes from "SELECT user FROM [Users]"
UI Element: Pass
- value comes from "SELECT pass FROM [Users] WHERE user = {User}"
UI Element: User Type
- value comes from "SELECT user_type FROM [Users] WHERE user = {User}"
Table: Users
| user | pass | user_type |
| bob | 123456 | admin |
| joe | 654321 | guest |
| alice | 123456 | admin |
Variant: Example 1
Given that I have either ~x~ or ~y~
...
Variant: Example 2
Given one of ~x~, ~y~
...
@comb-invalid(1)
Feature: Example
- value comes from "SELECT name FROM [MyDB].profession"
- value comes from "SELECT name
FROM [MyDB].profession"`
- value comes from
"""
SELECT name
FROM [MyDB].profession
"""
generate code (test scripts) for any programming language or testing framework able to perform functional tests for web, desktop or mobile applications.
Requirements for a plug-in
Its name must start with concordialang-, for example: concordialang-my-awesome-plugin.
It must be installable with NPM. We recommend you to publish it at NPM too.
Its package.json file must contain the property concordiaPlugin, like in the following example:
Properties of concordiaPlugin:
file: string: Relative path of the file that contains an implementation of the Plugin interface.
class: string: Class (name) that implementsPlugin.
serve: string: Multi-platform console command to execute in order to start the testing server. Whether the testing framework does not need running a testing server, you can set something like echo \"Running a testing server is not needed.\".
Let's get into the details on how to accomplish them.
Transforming Abstract Test Scripts into test scripts
Think of an Abstract Test Script (ATS) as an object to transform into source code. Basically, it contains a structure that represents the actions to be converted into commands of your preferred programming language.
For instance, consider the following action:
That could be converted to the following command in Codeception, for PHP:
Or to the following command in Cypress, for JavaScript:
Let's consider a more realistic example, starting with a Test Case in the hypothetical file feature1.testcase:
That would probably produce an Abstract Test Script like this:
A plug-in for Cypress could generate the following code from the above object:
To help generating such commands, you can use some template library such as Mustache (recommended) or Handlebars.
You can observe that after every command there is a line comment containing the corresponding location in the .testcase file. These comments will help to track the specification in case of test script failures.
Executing the test scripts
To execute the produced test scripts, you can use the target framework API or run the tool. Your call.
For example, whether you chose to run Cypress through npx and generate a report using Mocha JUnit Reporter, the command to run could be the following:
Transforming execution results
Now the plug-in needs to read the report and transform it into an object of the class TestScriptExecutionResult that will be returned to Concordia Compiler.
A plug-in must be able to track back the location of failures or errors in the corresponding .testcase file from a test script. When a test script has a failure or an error, the report saves the stack trace where it occurred. For instance, tests/feature1.js:20:4 indicates, respectively, the file, line and column where the problem happened. You can use these data to read the line of the test script file and retrieve the line comment that contains the corresponding line in the .testcase file.
You can use a regular expression to extract the data from the stack trace, like ReportConverter did in the method extractScriptLocationFromStackTrace. Then you can use the class FileInstrumentationReader from concordialang-plugin to retrieve the location from the line comment. Example:
You can read these locations in the examples described earlier. Whether tests/feature1.js:7:8 is...
Retrieving the line 10 and column 4 fromfeature1.testcase would give us:
In TestScriptExecutionResult, both test failures and errors are represented as an exception which includes the properties scriptLocation and specLocation.
Basic guide
We recommend you to follow these steps when creating a plug-in:
1. Open an issue to indicate that you are interested in developing a new plug-in for a certain testing framework or programming language. That is good way to make others aware of your work.
2. Choose a good name for your plug-in and check whether it registered at NPM. Remember that it must start with concordialang-. We recommend that it includes the target framework, tools or language. Example: concordialang-selenium-python to create a plug-in that generates test scripts for the Selenium framework and the Python language. You may use the opened issue (step 1) to get feedback about the name, if you wish.
3. Create a repository for it (e.g., at GitHub, GitLab, BitBucket) and clone the repository.
4. Create a NPM package with npm --init.
5. Add the propertyconcordiaPlugin to your package.json file.
7. Add your files. We recommend the following structure, but you can adapt it as you wish. For example, if you use TypeScript, you will probably add a tsconfig.json file. If you use Jest, your test folder will probably be named __tests__. We also recommend you to add a .editorconfig file and a .gitignore file (the latter should include the folder node_modules).
9. Test it with the Concordia Compiler before publishing it. Create a simple project that uses your plug-in. You can use NPM to install your plug-in from a directory in your computer or your remote repository. For example:
10. Publish your plug-in at NPM after making sure that you are using Semantic Versioning. Congratulations! Tell everybody and ask for feedback! ✌
import "feature1.feature"
@generated
@scenario(1)
@variant(1)
Test Case: Scenario 1 | V1
Given that I am on "https://myapp.com/register"
When I fill <#email> with "john.doe@example.com"
and I fill <#nome> with "John Doe"
and I click on <#register>
Then I see "Welcome, John Doe"
Directory to search for specification and test case files
recursive
boolean
true
Recursive directory search
encoding
string
"utf-8"
Default file encoding
extensions
array of string
[ ".feature" ]
File extensions to search
ignore
array of string
[]
Files to ignore
files
array of string
[]
Files to consider, instead of considering directory
Language
language
string
"en"
Default specification language
Plug-in
plugin
string
none
Plug-in to use
Processing
verbose
boolean
false
Verbose output
compileSpecification
boolean
true
Whether it is desired to compile the specification
generateTestCase
boolean
true
Whether it is desired to generate test case files
generateScript
boolean
true
Whether it is desired to generate test script files
executeScript
boolean
true
Whether it is desired to execute test script files
analyzeResult
boolean
true
Whether it is desired to analyze test script results
dirTestCase
string
same as features'
Output directory for test case files
dirScript
string
"./test"
Output directory for test script files
dirResult
string
"./output"
Output directory of test script results
lineBreaker
string
OS' default
Character(s) used to break lines in text files
Generation
caseUi
string
"camel"
String case used to identify UI Elements when their ids are not defined. Possible values are "camel", "pascal", "snake", "kebab", or "none".
caseMethod
string
"snake"
String case used for test scripts' methods. Possible values are "camel", "pascal", "snake", "kebab", or "none".
tcSuppressHeader
boolean
false
Whether it is desired to suppress header comments in test case files
tcIndenter
string
" "
Character(s) used as indenter for test case files
seed
string
current date and time
Randomic seed used by all the algorithms
randomMinStringSize
integer
0
Minimum size for random strings
randomMaxStringSize
integer
500
Maximum size for random strings
randomTriesToInvalidValue
integer
5
Tries to generate random values that are not in a set of values
Strategies
combVariant
string
"random"
Algorithm to select and to combine the Variants with the state needed by a certain Variant. Options are: "random" to pick a random Variant, "first" to pick the first one, "fmi" to pick the first most important, or "all" to combine them all.
combState
string
"sre"
Algorithm to select and to combine the Test Scenarios of every State of the selected Variants. Options are: "sre" to select a random Test Scenario of every State, "sow" to use the shuffled one-wise algorithm, "ow" to use the one-wise algorithm, or "all" to combine them all.
combInvalid
string
integer
"smart"
Number of UI Elements that will receive invalid data test cases at a time, e.g.1. String options are "none" for no invalid values, "smart" to let the compiler decide, "random" to select a random number of UI Elements, "all" to select all the invalid values.
combData
string
"sow"
Algorithm to combine data test cases. Options are: "sre" to select a random data test case to combine, "sow" to use the shuffled one-wise algorithm, "ow" to use the one-wise algorithm, or "all" to combine them all.
Abstract actions recognized through Natural Language Processing
Concordia Language contains a set of meta-actions that can be used by different testing frameworks for generating test scripts. Concordia Compiler uses Intent Recognition (yes, like chatbots do) to understand sentences and extract the desired action to perform. Later these actions are sent to testing frameworks for conversion into commands.
Concordia actions are recognized in Variants, Test Cases, and Test Events.
The following list presents the available actions and gives some examples on how to use them in sentences. We expect that you do not have to memorize them but, instead, see the things you can do. Try to write the sentences the way you speak, using first person singular (I) . When there is a UI Element in the sentence, embrace it with { and }, like {this}. The compiler will tell you whether it cannot understand some sentence you wrote. If you think it should have understood something, . We'll try to augment the compiler's vocabulary to understand it the next time.
accept
Accepts a browser message or app message.
amOn
Indicates a webpage or screen in which it is expected to be at.
append
Adds a value to an element.
attachFile
Attaches a file. It selects a file and confirms its choice (e.g., clicks Open).
cancel
Cancels a browser message or app message.
check
Checks a checkbox.
clear
Empties an input field or browser cookie
click
Clicks on something in the screen.
close
Closes a tab, a set of tabs, or an app (mobile only).
connect
Connects to a database.
The next sentence is for only:
disconnect
Disconnects from a database.
The next sentence is for only:
doubleClick
Performs a double click on something.
drag
Drags and drops something to a widget.
fill
Indicates that a field is being filled. If desired, a value can be given. Otherwise, a value will be generated for the corresponding Test Case.
fill + target
hide
Hides something.
install
Installs an app.
maximize
Maximizes a window or the browser.
move
Moves the mouse cursor to a place or element.
open
Opens something
press
Presses a key or key combination, separated by comma.
Some special keys (case sensitive!):
Add
Alt
ArrowDown or Down arrow
pull
Extracts a device's resource from a path.
refresh
Refreshes/reloads the current page.
remove
Removes/uninstall an app by its internal name (mobile only).
resize
Resizes a window.
rightClick
Performs a right click on something.
run
Runs a console command or a database script (SQL command).
👉 Available for only.
👉 Commands and SQL scripts must be declared between single quotes ('), in a single line.
👉 SQL scripts must reference their database.
SQL Syntax
Concordia Compiler uses to access databases and files through SQL-like commands. The supported SQL syntax may vary from one database to another. Please see the .
JSON and CSV as databases: INSERT accepts JSON objects or arrays as values. Example:
Excel and Firebase databases: Syntax similar to JSON and CSV databases. However, it has some limitations, as pointed out in :
SQL commands are limited to SELECT, UPDATE, INSERT and DELETE. WHERE works well. JOINs are not allowed. GROUP BY is not supported. LIMIT and OFFSET are combined into a single LIMIT syntax: LIMIT [offset,]number
INI databases: INSERT and DELETE are not supported yet by . UPDATE example:
SQLite databases: Currently uses that doesn't persist the changes in the file (only in memory).
saveScreenshot
Takes a screenshot an saves into a file.
scrollTo
Scrolls to a certain element.
see
Indicates that something can be seen. You can also negate the sentence to indicate something cannot be seen.
select
Selects a value for an element.
shake
Shakes the device - MOBILE ONLY.
swipe
Performs a swipe action - MOBILE ONLY
switch
Switches to a certain iframe and back to the application page; OR
Switches to a certain tab; OR
Switches an app to native mode or web mode - MOBILE ONLY.
NOTE: When switching to an iframe, all the commands that follow it will be applied to it.
tap
Performs a tap on an element - MOBILE ONLY
uncheck
Unchecks a checkbox.
wait
Wait for something.
Language Additions
This page tries to identify in which version of the Compiler a certain language construction became available.
👁🗨 "Planned" and "Available" refer to versions of the Compiler.
Construction
Planned
Available
#
0.1
*=Not used yet. **=It may not be released, since it is not needed.
0.x
#language
0.1
0.x
#locale
2.1
Not released yet.
@*
0.1
0.x
@scenario
0.1
0.x
@variant
0.1
0.x
@ignore
0.1
0.x
@importance
0.1
0.x*
@generated
0.1
0.x
@fail
0.1
0.x
@global
0.1
Not released yet.
@extends
0.1
Not released yet.
@category
0.1
Not released yet.**
@issue
0.1
Not released yet.**
@generate-only-valid-values
1.0
1.1
Feature
0.1
0.x
Scenario
0.1
0.x
Background
0.1
Not released yet.**
Variant
0.1
0.x
Variant Background
0.1
Not released yet.
~state~
0.1
0.x
Constants
0.1
0.x
Table
0.1
0.x
Database
0.1
0.x
UI Element
0.1
0.x
UI Element property locale
1.4
2.0
UI Element property locale format
1.4
2.0
UI Element properties with date and time expressions
# Simple alert
When I accept the alert
# Alert with a message
When I accept the alert "Do you want to continue?"
# Confirmation dialog
When I accept the confirmation
# Confirmation dialog with a message
When I accept the confirmation "Do you want to continue?"
# Popup dialog
When I accept the popup
# Popup dialog with a message
When I accept the popup "Are you sure?"
# Prompt dialog
When I accept the prompt
# Prompt dialog with a message
When I accept the prompt "What's your name?"
# URL
Given that I am on "http://concordialang.org"
# Contant
Given that I am on [Some Page]
# Append a value to a UI Element
When I append "Foo" to {Foo}
When I append 100 to {Bar}
When I append [My Contant] to {Zoo}
# Append a value to a UI literal
When I append "Bar" to <#foo>
When I append 200 to <#foo>
When I append [My Contant] to <#zoo>
When I attach the file "/path/to/file" to {Foo}
When I attach the file [My File] to {Foo}
When I attach the file "/path/to/file" to <#bar>
When I attach the file [My File] to <#bar>
When I cancel the alert
When I cancel the confirmation
When I cancel the popup
When I cancel the prompt
When I check {Foo}
When I check <#bar>
When I clear {Foo}
When I clear <#bar>
When I clear the cookie "app"
When I clear the cookie [App Cookie]
When I click on {Foo}
When I click on <#bar>
When I click on "Some Text"
When I click on [Value From Contant]
When I close the current tab
When I close the other tabs
# Mobile only
When I close the app
When I connect to the database [TestDB]
When I disconnect from the database [TestDB]
When I double click {Foo}
When I double click <#bar>
When I double click "Some Text"
When I double click [My Contant]
When I drag {Foo} to {Bar}
When I drag <#foo> to <#bar>
When I fill {Foo}
When I inform {Foo}
When I enter {Foo}
When I type {Foo}
When I fill {Foo} with "foo"
When I fill {Foo} with [My Contant]
When I fill <#bar> with "bar"
When I fill <#bar> with 3.1415
When I type "bar" in {Foo}
When I type "foo" in <#bar>
When I type 3.1416 in {Zoo}
When I type [My Contant] in <#zoo>
# Hides the device's keyboard - Mobile only
When I hide the keyboard
When I install the app "com.example.android.myapp"
When I maximize the window
When I move the cursor to {Foo}
When I move the cursor to <#bar>
When I move the cursor to {Foo} at 100, 200
# Opens the device's notifications panel - Mobile only
When I open the notifications panel
When I press "Enter"
When I press "Control", "Alt", "Delete"
When I press "Control", "S"
# Mobile only
When I pull "/storage/emulated/0/DCIM/logo.png" to "some/path"
When I refresh the page
When I refresh the current page
When I reload the page
When I reload the current page
# Mobile only
When I remove the app "com.example.android.myapp"
When I resize the window to 600, 400
When I right click {Foo}
When I right click <#bar>
When I right click "Some Text"
When I right click [Contant]
# Run a command in the console/terminal
When I run the command 'rmdir foo'
When I run the command './script.sh'
When I run the command [My Command]
# Run a SQL scripts in a database
When I run the script 'INSERT INTO [MyDB].product ( name, price ) VALUES ( "Soda", 1.50 )'
When I run the script 'INSERT INTO [MyDB].Users( UserName, UserSex, UserAge ) VALUES ( "Newton", "Male", 25 )'
When I run the script 'INSERT INTO [MyDB].`my long table name`( `long column`, `another long column`) VALUES ("Foo", 10)'
When I run the script 'UPDATE [MyDB].Users SET UserAge=26, UserStatus="online" WHERE UserName="Newton"'
When I run the script 'UPDATE [MyDB].`my long table name` SET `long column` = "Bar" WHERE `another long column` = 70'
When I run the script 'DELETE FROM [MyDB].Users WHERE UserName="Newton"'
When I run the script 'DELETE FROM [MyDB].`my long table name` WHERE `another long column` = 70'
When I run the script 'INSERT INTO [MyDB] VALUES { "name": "Mary", "surname": "Jane", "age": 21 }'
When I run the script 'UPDATE [MyDB] SET age = 22 WHERE name = "Mary"'
When I save a screenshot to "foo.png"
When I take a photo to "bar.png"
When I scroll to <#foo>
When I scroll to {Bar}
# Some text
Then I see "Some Text"
Then I see 3.1416
Then I see [Some Contant]
Then I do not see "Some Text"
Then I don't see [Some Contant]
Then see the text "Some Text"
Then I don't see the text "Some Text"
# UI Element or a UI literal
Then I see {Foo}
Then I don't see <#bar>
# Value is/isn't inside a UI Element or a UI literal
Then I see "hello" in {foo}
Then I see "world" in <#bar>
Then I don't see "hello" in {foo}
Then I don't see "world" in <#bar>
Then I do not see {Foo} with "hello"
Then I don't see <bar> with "world"
# Attribute from a UI Element or UI literal
Then I see {Foo} with the attribute "maxlength" valued "200"
Then I see the attribute "type" of <#bar> to be "text"
# CSS class or Style - WEB ONLY
Then I see {Foo} with the class "primary-button"
Then I see {Foo} with the style "color: blue"
# UI Element or ui literal is/isn't enabled
Then I see {Foo} is enabled
Then I see <#bar> is enabled
Then I do not see {Foo} is enabled
Then don't see <#bar> is enabled
# UI Element or ui literal is/isn't checked
Then I see {Foo} is checked
Then I see <#bar> is checked
Then I do not see {Foo} is checked
Then don't see <#bar> is checked
# Title
Then I see "Some Text" in the title
Then I don't see [My Constant] in the title
Then I see the title with "foo"
Then I don't see the title with [My Constant]
# URL
Then I see the url "/foo"
Then I don't see the url "/bar"
# Cookie
Then I see the cookie "foo"
Then I don't see the cookie [My Cookie]
# App is installed/not installed - MOBILE ONLY
Then I see that the app "com.example.android.myapp" is installed
Then I see that the app "com.example.android.myapp" is not installed
# An activity - MOBILE ONLY
Then I see that the current activity is ".HomeScreenActivity"
# Device locked/unlocked - MOBILE ONLY
Then I see that the device is locked
Then I see that the device is locked
# Device orientation - MOBILE ONLY
Then I see that the orientation is landscape
Then I see that the orientation is portrait
When I select "foo" in {Foo}
When I select [Contant] in <#bar>
When I shake the device
When I shake the phone
When I shake the tablet
# To a certain position
When I swipe <#io.selendroid.myapp:id/LinearLayout1> to 100, 200
# An UI Element/literal to another
When I swipe {Foo} to {Bar}
When I swipe <#foo> to <#bar>
# Down
When I swipe <#io.selendroid.myapp:id/LinearLayout1> down
# Up
When I swipe <#io.selendroid.myapp:id/LinearLayout1> up
# Left
When I swipe <#io.selendroid.myapp:id/LinearLayout1> left
# Right
When I swipe <#io.selendroid.myapp:id/LinearLayout1> right
# IFRAME
# To the first iframe
When I switch to the iframe
# To a certain iframe
When I switch to the iframe {My Frame}
When I switch to the iframe <#frame1>
# To an iframe inside another iframe
When I switch to the iframe {SubFrame 1} inside {Frame 1}
When I switch to the iframe <#subframe1> inside <#frame1>
# Back to the application page
When I switch to the application
When I switch to the page
# TAB
When I switch to the next tab
When I switch to the previous tab
When I switch to the tab 3
# MOBILE-WEB SWITCHING
# To native app
When I switch to native
# To web app
When I switch to web
When I tap {Confirm}
When I tap <~confirm>
When I unckeck {Foo}
When I uncheck <#bar>
# Unchecks an element inside another element
When I uncheck {Foo} in <#bar>
# -------
# ELEMENT
# -------
# An element
When I wait for {Foo}
When I wait for <#bar>
# An element to hide
When I wait {Foo} to hide
When I wait <#bar> to hide
# An element to be enabled
When I wait {Foo} to be enabled
When I wait <#bar> to be enabled
# An element to be visible
When I wait {Foo} to be visible
When I wait <#bar> to be visible
# An element to be invisible (same as to hide)
When I wait {Foo} to be invisible
When I wait <#bar> to be invisible
# Value inside element
When I wait for the value "foo" in {Foo}
When I wait for the value [My Constant] in <#bar>
# ----
# TEXT
# ----
When I wait for the text "Foo"
# ----
# URL
# ----
When I wait for the url "/foo"
# -------
# SECONDS
# -------
# Seconds
When I wait 1 second
When I wait 2 seconds
# Seconds for an element
When I wait 1 second for {Foo}
When I wait 5 seconds for <#bar>
# Seconds for an element to hide
When I wait 1 second for {Foo} to hide
When I wait 5 seconds for <#bar> to hide
# Seconds for an element to be enabled
When I wait 1 second for {Foo} to be enabled
When I wait 5 seconds for <#bar> to be enabled
# Seconds for an element to be visible
When I wait 1 second for {Foo} to be visible
When I wait 5 seconds for <#bar> to be visible
# Seconds for an element to be invisible (same as to hide)
When I wait 1 second for {Foo} to be invisible
When I wait 5 seconds for <#bar> to be invisible
# Value inside element
When I wait 1 second for the value "foo" in {Foo}
When I wait 5 seconds for the value [My Constant] in <#bar>
# Text
When I wait 3 seconds for the text "Foo"
# URL
When I wait 3 seconds for the url "/foo"