arrow-left
Only this pageAll pages
gitbookPowered by GitBook
1 of 25

Concordia

Loading...

Introduction

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Language

Loading...

Loading...

How It Works

Loading...

Loading...

Upgrade

Loading...

Loading...

Loading...

Loading...

Development

Loading...

Loading...

Loading...

How to Upgrade

hashtag
Versioning

Concordia Compiler's versions are based on Semantic Versioningarrow-up-right.

Although Semantic Versioning is conceived for APIarrow-up-rights 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.

  • UPDATE is increased when there are fixes, little changes or little novelties - all of them backwards-compatible.

Examples:

  • 0.2.0 is compatible with 0.1.0

  • 0.1.1 is compatible with 0.1.0

  • 1.0.0 is

hashtag
Upgrade

circle-check

NPM upgrades without breaking compatibility (when MINORorUPDATE changes).

circle-exclamation

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:

not
compatible with
0.2.0
Migration Guide
npm upgrade concordialang
npm upgrade -g concordialang

Overview of the Language

Overview of the Concordia declarations:

hashtag
Features and Scenarios

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.

hashtag
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

  • @

circle-check

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.

hashtag
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 .

hashtag
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:

circle-info

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.

hashtag
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

  • Constants

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.

circle-info

Don't worry about writing Test Cases. Write Variants and let Concordia generates the Test Cases for you.

hashtag
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.

circle-info

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:

circle-info

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

  • data type

circle-info

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:

circle-info

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.

hashtag
Import

A Import declaration allows you to import declarations from another .featurefile. Example:

Imported declarations are Feature, Constants, Table, Database, and UI Element.

hashtag
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.

hashtag
Table

A Table declares values in rows and columns to use in UI Element properties. Example:

circle-info

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.

hashtag
Database

A Database provides a way to indicate a connection to an existing . Example:

circle-info

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.

hashtag
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: executes once, before all the tests of the current 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 (').

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.

circle-info

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.

hashtag
Concordia metalanguage

Concordia metalanguagearrow-up-right 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 functionalarrow-up-right and non-functionalarrow-up-right requirements, although it produces only functional test cases. Because it adopts Agilearrow-up-right-style declarations, you don't have to write it so formally (like Use Casesarrow-up-right 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).

hashtag
Concordia compiler

Concordia compiler mix compiler arrow-up-righttechniques with machine learningarrow-up-right and natural language processingarrow-up-right 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.

Follow the Quick Start to see it in action! 😉

hashtag
Concordia plug-ins

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.

See the available plug-ins and how to create a plug-in for your favorite programming language and testing framework.

functional test scriptsarrow-up-right
equivalence partitioningarrow-up-right
boundary-value analysisarrow-up-right
random testingarrow-up-right
combinatorial testingarrow-up-right
databases and files
the name of a roman goddessarrow-up-right
(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]

  • 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

  • equal to
    string

    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.

  • User Storyarrow-up-right
    Given-When-Thenarrow-up-right
    User Storyarrow-up-right
    Given-When-Thenarrow-up-right
    Natural Language Processing (NLP)arrow-up-right
    action
    are documented
    welcomedarrow-up-right
    CSS query selectorsarrow-up-right
    XPatharrow-up-right
    Katalon-Concordiaarrow-up-right
    Variant
    Variant
    Variant
    import
    Test Cases
    Variant
    Test Cases
    State Based-Combination
    Test Case
    combination among Features
    test cases
    testing techniques
    test cases
    testing techniques
    test cases
    testing techniques
    SQLarrow-up-right
    database or file
    test cases
    testing techniques
    SQLarrow-up-right

    Using a Plug-in

    Concordia Compiler uses plug-ins for:

    1. setting the testing environment up;

    2. generating and executing test scripts;

    3. converting and reporting test results.

    Every plug-in can generate test scripts for a different testing framework or programming language.

    hashtag
    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.

    hashtag
    Installation

    Use --plugin-install plus the plug-in name. Example:

    circle-info

    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.

    hashtag
    Upgrade

    Just uninstall the plug-in and then install it again. Example:

    hashtag
    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:

    hashtag
    Commonly used commands

    circle-check

    You can omit the argument <plugin> if you have a configuration file with the property "plugin" defined.

    hashtag
    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:

    circle-info

    Naturally, you must replace <plugin> with the plugin name.

    The testing server will remain open. To stop it later, type Ctrl + C.

    hashtag
    Generating and executing test scripts

    circle-info

    Whether your plug-in needs a testing server, start it beforehand.

    hashtag
    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.

    hashtag
    Executing existing test scripts (without generating them)

    You can use --no-script to avoid generating test scripts. Only existing test scripts will be executed.

    hashtag
    See also

    Quick Start

    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 .

    circle-info

    See in case you don't know it yet.

    hashtag
    System Requirements

    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: Sales Report
    
    Scenario: Daily sales report
    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]>
    When I click on {Continue}
    UI Element: Continue
      - locator: //*[@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'

    Concordia Compiler requires only NodeJSarrow-up-right 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 Google Chromearrow-up-right. Therefore, download it before continuing.

    hashtag
    Step 0: Initialize

    If you are starting a new project or whether your project does not have a file named package.json, then run:

    circle-info

    You can use NPM, Yarn or PNPM.

    hashtag
    Step 1: Install

    Install Concordia Compiler locally:

    triangle-exclamation

    It will install version 2 that is in alpha stage, although stable.

    circle-exclamation

    Note that the needed package name is concordialang (with lang). After installing it, we'll always use npx concordia to execute it.

    hashtag
    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 plug-in and database drivers.

    hashtag
    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 visitor).

    hashtag
    Step 4: Execute

    Finally, run

    circle-check

    ✨ 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

    • report the test script results.

    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 change itarrow-up-right 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) (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.

    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:

    circle-info

    Concordia Compiler can also generate test dataarrow-up-right and test oraclesarrow-up-right for you. All the test cases and test scripts receive line comments that detail the kind of data test case being explored and reference declarations used to produce oracles and test data.

    hashtag
    Notes on installation options

    You can opt to install Concordia Compiler locally (per project) or globally.

    hashtag
    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 NPXarrow-up-right before every command.

    Follow the Quick Start for a install installation.

    circle-info

    NPXarrow-up-right 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:

    • How to install globally with NPM on Linux or MacOS without sudoarrow-up-right.

    • 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.

    hashtag

    hashtag

    Slack channelarrow-up-right
    Understanding Concordia
    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)
    });
    
    npm init --yes
    npm i -D concordialang
    npx concordia --init
    mkdir features
    npx concordia
     ┃
     ┣ features/          
     ┃ ┣ search.feature
     ┃ ┗ search.testcase     🡐 generated test case
     ┣ node_modules/
     ┣ test/              
     ┃ ┗ search.js           🡐 generated test script
     ┣ .concordiarc
     ┣ codecept.json
     ┣ package.json  
     ┗ package-lock.json
    sudo npm install -g concordialang
    concordia --version

    Yes, Java 8+

    Yes

    Android, iOS, Windows

    It depends**

    Yes

    codeceptjs-playwright uses the frameworks CodeceptJSarrow-up-right and Playwrightarrow-up-right,

    requires Node 10.14 or above, and works with Chromium, Firefox, and Safari.

  • codeceptjs-webdriverio uses the frameworks CodeceptJSarrow-up-right and WebDriverIOarrow-up-right, and works with Chrome, Firefox, IE, and Edge.

  • codeceptjs-appium uses the frameworks CodeceptJSarrow-up-right and Appiumarrow-up-right, and requires some additional configurationarrow-up-right to work.

  • codeceptjs-testcafe and codeceptjs-playwright are only available for Concordia Compiler 2 or above.

  • plugin to use a plug-in

  • plugin-info to show information about a plug-in

  • plugin-list to list installed plug-ins

  • Name (concordialang-...)

    Target platform

    Needs Java?

    RTS*

    codeceptjs-testcafearrow-up-right

    web

    No

    No

    codeceptjs-playwrightarrow-up-right

    web

    No

    No

    codeceptjs-webdriverioarrow-up-right

    Testing Server
    CodeceptJSarrow-up-right
    TestCaféarrow-up-right
    configuration file
    Configuration file
    Commands for plug-ins
    Creating a plug-in

    web

    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.

    hashtag
    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 can indicate the .feature files to be ignored, when a directory is given.

    hashtag
    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:

    1. Precondition: when declared in a Given sentence;

    2. State Call: when declared in a When sentence;

    3. Postcondition: when declared in a Then sentence.

    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:

    1. Select the Variants to combine;

    2. Generate successful test scenarios for the selected Variants;

    3. Select the successful test scenarios to combine;

    4. Generate (successful and unsuccessful) test scenarios for the current Variant;

    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.

    hashtag
    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: Selects the first most important Variant (since two Variants can have the same importance value) that produces the required State;

    Example:

    hashtag
    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.

    • ow

    Example:

    hashtag
    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.

    hashtag
    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.

    hashtag
    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:

    hashtag

    hashtag
    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:

    hashtag
    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;

    • When the property value

    circle-info

    There is more logic involved for generating these values. ...

    hashtag
    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:

    1. FILLED: a pseudo-random double value is generated;

    2. 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:

    1. LOWEST_VALUE: the lowest possible double is used

    2. RANDOM_BELOW_MIN_VALUE: a random double before the minimum value is generated

    3. JUST_BELOW_MIN_VALUE: a double just below the minimum value is used (e.g., 999.99)

    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:

    1. MEDIAN_VALUE: the median between the minimum and the maximum values

    2. RANDOM_BETWEEN_MIN_MAX_VALUES: a pseudo-random double value between the minimum and the maximum values

    3. JUST_BELOW_MAX_VALUE: the value just below the maximum value

    The tests from 5 to 7 will produce values considered invalid.

    hashtag
    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.

    hashtag
    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.

    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" for generating test scripts; the directory "test/e2e" for test scripts; and the directory

    Actions

    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.

    circle-info

    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

    npx concordia --plugin-install codeceptjs-testcafe
    npx concordia --plugin-uninstall codeceptjs-playwright
    npx concordia --plugin-install codeceptjs-playwright 
    {
      "plugin": "codeceptjs-playwright"
    }
    
    npx concordia --plugin-serve <plugin>
    npx concordia --plugin <plugin>
    npx concordia --plugin <plugin> --no-run --no-result
    npx concordia --plugin <plugin> --no-script
    codeceptjs-appiumarrow-up-right

    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).

  • 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).

  • all: Selects all the Variants that produce the required State.

    :
    selection;
  • all: Selects all the successful test scenarios to combine.

  • Exactly the minimum value

    JUST_ABOVE_MIN_VALUE

    The value just above the minimum value, considering the data type and decimal places if applicable

    ZERO_VALUE

    Zero (0)

    MEDIAN_VALUE

    The median between the minimum value and the maximum value

    RANDOM_BETWEEN_MIN_MAX_VALUES

    A random value between the minimum value and the maximum value

    JUST_BELOW_MAX_VALUE

    The value just below the maximum value, considering the data type and decimal places if applicable

    MAX_VALUE

    Exactly the maximum value

    JUST_ABOVE_MAX_VALUE

    The value just above the maximum value, considering the data type and decimal places if applicable

    RANDOM_ABOVE_MAX_VALUE

    A random value above the maximum value

    GREATEST_VALUE

    The greatest value for the data type, e.g., greatest integer

    LENGTH

    LOWEST_LENGTH

    An empty string

    RANDOM_BELOW_MIN_LENGTH

    A string with random characters and random length, less than the minimum length

    JUST_BELOW_MIN_LENGTH

    A string with random characters and length exactly below the minimum length

    MIN_LENGTH

    A string with random characters and length exactly equal to the minimum length

    JUST_ABOVE_MIN_LENGTH

    A string with random characters and length exactly above the minimum length

    MEDIAN_LENGTH

    A string with random characters and length equal to the median between the minimum length and the maximum length

    RANDOM_BETWEEN_MIN_MAX_LENGTHS

    A string with random characters and random length, between the minimum length and the maximum length

    JUST_BELOW_MAX_LENGTH

    A string with random characters and length exactly below the maximum length

    MAX_LENGTH

    A string with random characters and length exactly equal to the maximum length

    JUST_ABOVE_MAX_LENGTH

    A string with random characters and length exactly above the maximum length

    RANDOM_ABOVE_MAX_LENGTH

    A string with random characters and random length, greater than the maximum length

    GREATEST_LENGTH

    The greatest length supported for a string (see Notes)

    FORMAT

    VALID_FORMAT

    A value that matches the defined regular expression

    INVALID_FORMAT

    A value that does not match the defined regular expression

    SET

    FIRST_ELEMENT

    The first element in the defined set or query result

    RANDOM_ELEMENT

    A random element in the defined set or query result

    LAST_ELEMENT

    The last element in the defined set or query result

    NOT_IN_SET

    A value that does not belong to the defined set or query result

    REQUIRED

    FILLED

    A random value

    NOT_FILLED

    Empty value

    COMPUTED

    RIGHT_COMPUTATION

    A value generated by the defined algorithm

    WRONG_COMPUTATION

    A value different from that generated by the defined algorithm

    is declared:
    • if it comes from a set of values (inclusing a query result), all the DTC of the group SET are applied;

    • otherwise, ...

  • When the property minimum value is declared:

  • MIN_VALUE: the minimum value is used

  • JUST_ABOVE_MIN_VALUE: a double just above the minimum value is used (e.g., 1000.01)

  • ZERO_VALUE: zero (0) is used

  • MAX_VALUE: the maximum value

  • JUST_ABOVE_MAX_VALUE: the value just above the maximum value

  • RANDOM_ABOVE_MAX_VALUE: a pseudo-random double above the maximum value

  • GREATEST_VALUE: the greatest possible double

  • RANDOM_ELEMENT
  • LAST_ELEMENT

  • NOT_IN_SET

  • Group

    Data Test Case

    Description

    RANGE

    LOWEST_VALUE

    The lowest value for the data type, e.g., lowest integer

    RANDOM_BELOW_MIN_VALUE

    A random value below the minimum value

    JUST_BELOW_MIN_VALUE

    The value just below the minimum value, considering the data type and decimal places if applicable

    one-wisearrow-up-right
    Boundary-value analysisarrow-up-right
    Equivalence partitioningarrow-up-right
    Random testingarrow-up-right
    Special casesarrow-up-right
    UI Element
    user interface element
    test oraclearrow-up-right

    MIN_VALUE

    One-wisearrow-up-right
    ) . 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.

    hashtag
    accept

    Accepts a browser message or app message.

    hashtag
    amOn

    Indicates a webpage or screen in which it is expected to be at.

    hashtag
    append

    Adds a value to an element.

    hashtag
    attachFile

    Attaches a file. It selects a file and confirms its choice (e.g., clicks Open).

    hashtag
    cancel

    Cancels a browser message or app message.

    hashtag
    check

    Checks a checkbox.

    hashtag
    clear

    Empties an input field or browser cookie

    hashtag
    click

    Clicks on something in the screen.

    hashtag
    close

    Closes a tab, a set of tabs, or an app (mobile only).

    hashtag
    connect

    Connects to a database.

    The next sentence is for Test Eventsarrow-up-right only:

    hashtag
    disconnect

    Disconnects from a database.

    The next sentence is for Test Eventsarrow-up-right only:

    hashtag
    doubleClick

    Performs a double click on something.

    hashtag
    drag

    Drags and drops something to a widget.

    hashtag
    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.

    hashtag
    fill + target

    hashtag
    hide

    Hides something.

    hashtag
    install

    Installs an app.

    hashtag
    maximize

    Maximizes a window or the browser.

    hashtag
    move

    Moves the mouse cursor to a place or element.

    hashtag
    open

    Opens something

    hashtag
    press

    Presses a key or key combination, separated by comma.

    Some special keys (case sensitive!):

    • Add

    • Alt

    • ArrowDown or Down arrow

    • ArrowLeft or Left arrow

    • ArrowRight or Right arrow

    • ArrowUp or Up arrow

    • Backspace

    • Command

    • Control

    • Del

    • Divide

    • End

    • Enter

    • Equals

    • Escape

    • F1 to F12

    • Home

    • Insert

    • Meta

    • Multiply

    • Numpad 0 to Numpad 9

    • Pause

    • Pagedown or PageDown

    • Pageup or PageUp

    • Semicolon

    • Shift

    • Space

    • Subtract

    • Tab

    hashtag
    pull

    Extracts a device's resource from a path.

    hashtag
    refresh

    Refreshes/reloads the current page.

    hashtag
    remove

    Removes/uninstall an app by its internal name (mobile only).

    hashtag
    resize

    Resizes a window.

    hashtag
    rightClick

    Performs a right click on something.

    hashtag
    run

    Runs a console command or a database script (SQL command).

    👉 Available for Test Eventsarrow-up-right only.

    👉 Commands and SQL scripts must be declared between single quotes ('), in a single line.

    👉 SQL scripts must reference their database.

    hashtag
    SQL Syntax

    Concordia Compiler uses database-jsarrow-up-right to access databases and files through SQL-like commands. The supported SQL syntax may vary from one database to another. Please see the documentation of the corresponding driverarrow-up-right.

    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 its documentationarrow-up-right :

    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 database-js-iniarrow-up-right. UPDATE example:

    SQLite databases: Currently database-js-sqlitearrow-up-right uses sql.jsarrow-up-right that doesn't persist the changes in the file (only in memory).

    hashtag
    saveScreenshot

    Takes a screenshot an saves into a file.

    hashtag
    scrollTo

    Scrolls to a certain element.

    hashtag
    see

    Indicates that something can be seen. You can also negate the sentence to indicate something cannot be seen.

    hashtag
    select

    Selects a value for an element.

    hashtag
    shake

    Shakes the device - MOBILE ONLY.

    hashtag
    swipe

    Performs a swipe action - MOBILE ONLY

    hashtag
    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.

    hashtag
    tap

    Performs a tap on an element - MOBILE ONLY

    hashtag
    uncheck

    Unchecks a checkbox.

    hashtag
    wait

    Wait for something.

    please tell usarrow-up-right
    Given that I have ~payment method selected~
    npx concordia --comb-variant=all
    npx concordia --comb-state=all
    npx concordia --random-max-string-size=300
    "randomMaxStringSize": 300
    UI Element: Salary
      - data type is double
    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   |
    Table: professions
      | name       | min_salary | max_salary |
      | Lawyer     | 100000     |  900000    |
      | Accountant |  90000     |  800000    |
      | Dentist    | 150000     |  900000    |
      | Professor  |  80000     |  500000    |
      | Mechanic   |  50000     |  120000    |
    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"
     # 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"
     
    "test/e2e/output"
    for output files such as report files and screenshots.
    circle-exclamation

    CLI options take precedence over values from the configuration file

    hashtag
    File-related options

    hashtag
    directory

    Type

    Default value

    CLI option

    string

    "." (current dir)

    --directory

    Directory to search for .feature and .testcase files.

    Example:

    hashtag
    recursive

    Type

    Default value

    CLI option

    boolean

    true

    --recursive

    Use recursive directory search.

    Example:

    hashtag
    files

    Type

    Default value

    CLI option

    string or string array

    (none)

    --files

    Files to consider, instead of considering directory. The files must be separated by colon.

    Example:

    Another example:

    circle-info

    We recommend you to configure the property directory whether your project can have new files.

    hashtag
    ignore

    Type

    Default value

    CLI option

    string or string array

    (none)

    --ignore

    Files to ignore, considering the parameter directory. The files must be separated by colon. Example:

    hashtag
    extensions

    To-Do

    hashtag
    encoding

    Type

    Default value

    CLI option

    string

    "utf8"

    --encoding

    File encoding of the files. Accepted values are:

    • utf8 or utf-8

    • latin1

    • ascii

    • ucs2 or ucs-2

    • utf16le or utf-16le

    Naturally, the files must be edited with the given file encoding.

    hashtag
    Language-related options

    To-Do

    hashtag
    Plug-in related options

    To-Do

    hashtag
    Processing-related options

    To-Do

    hashtag
    Generation-related options

    To-Do

    hashtag
    Strategy-related options

    To-Do

    Category

    Option

    Data Type

    Default value

    Description

    Files

    directory

    JSON formatarrow-up-right
    {
        "language": "pt",
        "directory": "docs/features",
        "plugin": "codeceptjs-testcafe",
        "dirScript": "test/e2e",
        "dirResult": "test/e2e/output"
    }

    Migration Guide

    Migrations are only needed when upgrading to a major version. See Versions and Upgrade for more information.

    To upgrade to a major version:

    1. Uninstall the current version, e.g.: npm uninstall -D concordialang

    2. Install the new version, e.g.: npm install -D concordialang

    3. Now proceed as described below (depending on your current version).

    Note: For a global installation, replace -D with -g.

    hashtag
    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 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 to install the proper driver(s) for your application.

    hashtag
    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 , currently codeceptjs-webdriverio or codeceptjs-appium. Example:

    hashtag
    See also

    What's Next

    Ideas and proposals

    Work in progress and issues:

    • Kanban Boardarrow-up-right (GitHub)

    • Issuesarrow-up-right (GitHub)

    hashtag
    Logo

    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:

    circle-info

    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.

    hashtag
    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)

    hashtag
    Language

    hashtag
    Variant Background

    circle-info

    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.

    hashtag

    hashtag
    Constant names without quotation marks

    Like this:

    MOTIVATION: Simpler syntax.

    hashtag

    hashtag
    Visual comparison

    Please see .

    MOTIVATION: It allows to perform visual comparison and detect related bugs.

    hashtag

    hashtag
    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.

    hashtag

    hashtag
    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).

    hashtag

    hashtag
    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.

    hashtag

    hashtag
    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.

    hashtag

    hashtag
    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.

    hashtag

    hashtag
    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.

    hashtag

    hashtag
    Multi-line queries

    Currently:

    Proposal (to accept as valid):

    Alternative proposal:

    MOTIVATION: Make it easier to write/read SQL statements.

    hashtag
    Compiler

    hashtag
    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:

    hashtag
    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).

    hashtag

    hashtag
    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.

    hashtag

    hashtag
    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.

    hashtag

    hashtag
    UI and Report

    circle-exclamation

    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

    hashtag
    Tool integration

    hashtag
    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.

    hashtag
    Integration with reporting tools

    • Create integration with . Other reporters (e.g., ) can be added further.

    hashtag

    hashtag
    Performance improvements

    hashtag
    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:

    hashtag
    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.

    Related things

    hashtag
    Tools

    • Katalon-Concordiaarrow-up-right is a browser extension for Chrome and Firefox that converts recordings from Katalon Recorderarrow-up-right (which is a capture-replay toolarrow-up-right - 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.

    hashtag
    Publications

    1. 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.

    2. 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:

    A Small Example

    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: Login
      As a user
      I would like to authenticate myself
      In order to access the application
    
    

    These features could correspond to the following sketches:

    circle-info

    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 application in 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.

    hashtag
    Execution without generation

    This will not generate test cases or test scripts, but it will execute them and get their results:

    hashtag
    Output

    login.testcase:

    test/login.js:

    hashtag
    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!

    hashtag
    See also

    Breaking Changes

    Information about breaking changes.

    hashtag
    From 1.x to 2.x

    Please read Issue #56arrow-up-right.

    hashtag
    From 0.x to 1.x

    hashtag
    Concordia Compiler

    1. It changed the way that all the plug-in operations are handled. See for details.

    2. The behavior of the following plug-in commands were affected:

    👉 See the to know the commands' syntax.

    hashtag
    Concordia Language

    No compatibility breaks.

    hashtag
    Other relevant changes

    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.

    hashtag
    FAQ

    1. Were there any changes in commands' syntaxes?

      No, there were not.

    2. 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 .

    Command Line Interface

    CLI

    Basic syntax is:

    where:

    • A parameter between[ and ] means that it is optional.

    • [dir] is the directory that contains your .feature files. Whether it is not informed, the tool searches all the subdirectories from the current path.

    • [options] changes how the tool behaves.

    You can run concordia --help to see all the command line interface (CLI) options.

    circle-exclamation

    You need to use npx before the commands if you opted for a .

    hashtag
    Configuration

    hashtag
    --config

    Indicates a

    Alias: -c

    By default the configuration file is loaded from the current directory.

    hashtag
    --init

    Creates a 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 (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.

    circle-info

    CLI options take precedence over values from .

    hashtag
    --save-config

    Add CLI parameters to the configuration file

    The following example will consider the given directory and add it to the configuration file:

    hashtag
    Directories and files

    hashtag
    --directory

    Indicates the directory that has .feature and .testcase files.

    Alias: -d

    When not given, it will search the current directory and all subdirectories recursively.

    circle-info

    We strongly recommend to use it or set it in the .

    Example:

    This is equivalent to:

    circle-info

    You can use it with to disable recursive search.

    hashtag
    --file

    Specifies .feature files to consider

    Aliases: -f, --files

    Files must be separated by comma.

    Whether the command also informs a directory (e.g. ), it searches the files inside the directory. Example:

    The example above will search for ./feature/f1.feature and ./feature/f1.feature.

    hashtag
    --ignore

    Specify .feature files to ignore when a directory is informed

    Alias: -i

    Files must be separated by comma.

    hashtag
    --no-recursive

    Disables recursive search when a directory is informed

    hashtag
    Output directories

    circle-info

    The directories will be used by a plug-in. We strongly recommend that you specify them in a .

    hashtag
    --dir-result

    Specifies the directory for test reports and screenshots.

    Alias: -O

    hashtag
    --dir-script

    Specifies the directory for test script files.

    Alias: -o

    hashtag
    Language and locale

    hashtag
    --language

    Sets the default language to consider.

    Alias: -l

    The language code must have only 2 characters and needs to be one of the .

    hashtag
    --language-list

    Lists the available language codes.

    hashtag
    --locale-list

    Lists the available locales.

    hashtag
    Processing and output

    hashtag
    --just-script

    Just generates test scripts

    hashtag
    --just-spec

    Just verifies specification files

    hashtag
    --just-test-case

    Just generates test cases

    hashtag
    --just-run

    Just execute test script files

    hashtag
    --no-script

    Avoids generating test scripts

    hashtag
    --no-spec

    Avoids processing specification files

    hashtag
    --no-test-case

    Avoids generating test cases

    hashtag
    --no-result

    Avoids reading test scripts' results

    hashtag
    --no-run

    Avoids running test scripts

    circle-info

    Use -x to combine --no-run and --no-result

    hashtag
    --verbose

    Uses a verbose output

    hashtag
    Test script execution

    circle-info

    These parameters require a plugin () and some plug-ins may not support them.

    hashtag
    --instances

    Number of parallel instances to execute.

    Alias: -I

    hashtag
    --headless

    Executes browsers in .

    Alias: -H

    hashtag
    --script-file

    Sets one or more test script files to execute

    Aliases: -F, --script-files

    Files must be separated by comma.

    hashtag
    --script-grep

    Sets a text or regular expression to filter the files to execute

    Alias: -G

    hashtag
    --target

    Sets target browsers or platforms to execute

    Alias: -T

    Browser or platforms must be separated by comma.

    hashtag
    Plug-in

    circle-info

    You can omit the prefix concordialang- from plug-in names. For instance, concordialang-codeceptjs-playwright becomescodeceptjs-playwright.

    hashtag
    --plugin

    Uses the given plug-in (if installed)

    Alias: -p

    circle-info

    You can use it with -x to avoid running test scripts and reading their output:

    hashtag
    --plugin-about

    Shows information about an installed plug-in

    Alias: --plugin-info

    hashtag
    --plugin-install

    Installs a plug-in

    hashtag
    --plugin-list

    Lists installed plug-ins

    hashtag
    --plugin-serve

    Starts a testing server for a plug-in

    Alias: -S

    The testing server will keep running. Thus, it's a good idea to open a separate terminal/console for it. To stop it later, type Ctrl + C.

    hashtag
    --plugin-uninstall

    Uninstalls a plug-in

    hashtag
    Database

    circle-info

    See for the list of available databases.

    hashtag
    --db-install

    Installs a database driver

    hashtag
    --db-list

    Lists installed databases

    hashtag
    --db-uninstall

    Uninstalls a database driver

    hashtag
    Random generation

    hashtag
    --random-max-string-size

    Maximum random string size to generate, when free values are allowed.

    The default value is 500 .

    hashtag
    --random-min-string-size

    Minimum random string size to generate, when free values are allowed.

    The default value is 0 .

    hashtag
    --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.

    The default value is 5 .

    hashtag
    --seed

    Sets a

    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.

    circle-info

    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.

    hashtag
    Combination strategies

    --comb-data

    --comb-invalid

    --comb-state

    --comb-variant

    hashtag
    File formats and extensions

    circle-info

    You probably don't need to use them, unless you're facing problems to read files.

    hashtag
    --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.

    Note on line breakers:

    • Linux uses "\n"

    • MacOS uses "\r"

    • Windows uses "\r\n"

    hashtag
    --encoding

    Sets the expected file encoding

    Alias: -e

    The default is utf8. Possible values are:

    • ascii

    • latin1

    • ucs2 or ucs-2

    hashtag
    Information

    hashtag
    --about

    Shows information about the compiler.

    hashtag
    --help

    Shows the available commands

    hashtag
    --newer

    Verify if there is a newer version.

    hashtag
    --version

    Shows the current version.

    Using Databases

    Concordia can use databases to retrieve test data or to set the testing environment up before or after running test scripts.

    hashtag
    Installation

    hashtag
    Installing

    circle-info

    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.

    circle-info

    You can also install or uninstall them with NPM, prefixing the driver name with database-js-. Example: npm i -D database-js-mysql.

    hashtag
    Uninstalling

    Use --db-uninstall plus the driver name. Example:

    hashtag
    Upgrading

    Just uninstall and then install it again.

    hashtag
    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.

    hashtag
    See also

    Overview

    To cite Concordia in a paper, please cite its PhD Thesis:

    circle-info

    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.

    hashtag
    Usage cycle

    We recommend the following usage cycle:

    1. Write or update your requirements specification with the Concordia Language and validate them with users or stakeholders;

    2. Use Concordia Compiler for generating functional tests from the specification and running them;

    3. 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.

    hashtag
    Process

    When you type concordia, the compiler performs the following process:

    1. It reads your .feature and .testcase files, and uses a and a to identify and check documents' structure.

    2. It uses (NLP) to identify sentences' , which increases the chances of recognizing different writing styles.

    3. It performs to check recognized declarations.

    Creating a Plug-in

    hashtag
    Introduction

    Concordia Compiler uses plug-ins for generating test scripts, running them and reading their results.

    A plug-in can...

    • be coded in JavaScript or , such as (recommended),

    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.

    Concordia Language

    Informal specification

    hashtag
    Line comments

    hashtag
    Common line comment

    It makes the compiler to ignore a piece of text. Example:

    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.

    npx concordia --init
    {
      "directory": "./features"
    }
    {
      "recursive": false
    }
    {
      "files": "/path/to/file1.feature,/other/file2.feature"
    }
    {
      "files": [
        "/path/to/file1.feature",
        "/other/file2.feature"
      ]
    }
    {
      "ignore": [
        "/path/to/file1.feature",
        "/other/file2.feature"
      ]
    }
    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"
    
    concordia [dir] [options]

    string

    none

    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.

    XPatharrow-up-right
    CLUIarrow-up-right
    cc-grapharrow-up-right
    https://doi.org/10.1145/3323503.3360639arrow-up-right

    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.

    Is it possible now to install or uninstall a plug-in with NPM ?

    Yes, it is possible to do both now.

    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.

    plugin-install

    It installs a plug-in and its dependencies.

    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

    Issue #34arrow-up-right
    Command Documentationarrow-up-right
    update it

    It shows information about a plug-in installed for the application.

    amOn

    amOnPage

    goto

    check

    checkOption

    check

    clear + element

    clearField

    N/A

    clear + cookie

    clearCookie

    clearCookies

    click

    click

    doubleClick

    doubleClick

    dblclick

    fill

    fill

    N/A

    focus

    move

    moveTo

    moveCursorTo

    trigger

    hover

    hover

    hover

    press

    press

    refresh

    refreshPage

    reload

    resize

    resizeWindow

    setViewportSize

    rightClick

    rightClick

    saveScreenshot

    saveScreenshot

    screenshot

    scrollTo

    scrollTo

    scrollTo

    select

    selectOption

    selectOption

    switch + element

    switchTo

    N/A

    switch + next tab

    switchToNextTab

    N/A

    switch + previous tab

    switchToPreviousTab

    N/A

    tap

    tap

    N/A

    type

    type

    wait + seconds

    wait

    N/A

    wait + element

    waitForElement

    waitForElement

    uncheck

    uncheckOption

    uncheck

    Concordia

    Action [+target]

    Appium

    CodeceptJS

    Cypress

    Playwright

    Puppeteer

    Slackarrow-up-right
    GitHubarrow-up-right

    TestCafé

    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

    1.4

    2.0

    Test Case

    0.1

    0.x

    Before All

    0.1

    0.x

    After All

    0.1

    0.x

    Before Feature

    0.1

    0.x

    After Feature

    0.1

    0.x

    Before Each Scenario

    0.1

    0.x

    After Each Scenario

    0.1

    0.x

    *=Not used yet. **=It may not be released, since it is not needed.

    Construction

    Planned

    Available

    #

    0.1

    0.x

    #language

    0.1

    0.x

    #locale

    new plug-ins
    Database Drivers
    available plug-ins
    Information about the breaking changes
    Concordia's version numbering

    utf16le or utf-16le

  • utf8 or utf-8

  • local installation
    configuration file
    configuration file
    configuration file
    configuration files
    configuration file
    --no-recursive
    --directory
    configuration file
    available languages
    --plugin
    headless modearrow-up-right
    Using Databases
    random seedarrow-up-right

    json

    JSON files

    Already installed

    mysql

    MySQL

    adodb

    MS Access

    Windows only

    mssql

    MS SQL Server

    postgres

    PostgreSQL

    sqlite

    SQLite

    Driver Name

    Database

    note

    csv

    CSV files

    xlsx

    Excel files

    firebase

    Firebase

    ini

    Database
    Supported syntax
    Declaring Tables

    INI files

    Ruby: Capybaraarrow-up-right (web), Calabasharrow-up-right (mobile applications developed with Xamarinarrow-up-right)

  • Python: Seleniumarrow-up-right (web)

  • C++: QtTestarrow-up-right (desktop), DUnitarrow-up-right (desktop)

  • Delphi: DUnitXarrow-up-right (desktop), DUnitarrow-up-right (desktop)

  • Other programming languages or frameworks are welcome! 💖

  • by importance value (tag @importance)

  • Test Case for test script generation;

    • by importance value (tag @importance)

  • Consider global UI Elements

    Make the tool processing UI Elements tagged with @global.

    Allow inheritance of UI Elements

    Use @extends( <name> ) to extend another UI Element.

    Example:

  • Multiple declared Tables per query: Allow a query to reference more than one table declared in the specification.

  • Multiple declared Databases per query: Allow a query to reference more than one database declared in the specification.

  • Slack Channelarrow-up-right
    open an Issuearrow-up-right
    the name of a roman goddessarrow-up-right
    how to create a plug-in
    Cypressarrow-up-right
    Cypressarrow-up-right
    MacacaJSarrow-up-right
    Codeceptionarrow-up-right
    Seleniumarrow-up-right
    Robotiumarrow-up-right
    AssertJ Swingarrow-up-right
    Issue #27arrow-up-right
    @ignore
    list of naughty stringsarrow-up-right
    gherkin-autocompletearrow-up-right
    Allurearrow-up-right
    Macaca Reporterarrow-up-right
    Worker Threadsarrow-up-right
    , or
    .
  • generate code (test scripts) for any programming language or testing framework able to perform functional testsarrow-up-right for web, desktop or mobile applications.

  • hashtag
    Requirements for a plug-in

    1. Its name must start with concordialang-, for example: concordialang-my-awesome-plugin.

    2. It must be installable with NPMarrow-up-right. We recommend you to publish it at NPM too.

    3. It must implement the interface Pluginarrow-up-right from concordialang-pluginarrow-up-right.

    4. 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.\".

    hashtag
    Tasks to perform

    A plug-in must deal with three tasks:

    1. Transforming Abstract Test Scriptsarrow-up-right into test scripts (i.e., source code).

    2. Executing the produced test scripts, regarding some execution optionsarrow-up-right.

    3. Transforming test execution results (produced by the testing framework) to the expected resulting formatarrow-up-right.

    Let's get into the details on how to accomplish them.

    hashtag
    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 Codeceptionarrow-up-right, for PHP:

    Or to the following command in Cypressarrow-up-right, 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 Mustachearrow-up-right (recommended) or Handlebarsarrow-up-right.

    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.

    hashtag
    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,arrow-up-right the command to run could be the following:

    hashtag
    Transforming execution results

    Now the plug-in needs to read the report and transform it into an object of the class TestScriptExecutionResultarrow-up-right that will be returned to Concordia Compiler.

    For instance, the core of the plug-in for CodeceptJSarrow-up-right reads the report from a JSON file generated by using CodeceptJSarrow-up-right with Mocha Multi Reportersarrow-up-right. The current implementation is available in the class ReportConverterarrow-up-right.

    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 ReportConverterarrow-up-right did in the method extractScriptLocationFromStackTrace. Then you can use the class FileInstrumentationReaderarrow-up-right from concordialang-pluginarrow-up-right 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 TestScriptExecutionResultarrow-up-right, both test failures and errors are represented as an exception which includes the properties scriptLocation and specLocation.

    hashtag
    Basic guide

    We recommend you to follow these steps when creating a plug-in:

    1. Open an issuearrow-up-right 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 NPMarrow-up-right. 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 property concordiaPlugin to your package.json file.

    6. Install concordialang-pluginarrow-up-right as a dependency:

    7. Add your files. We recommend the following structure, but you can adapt it as you wish. For example, if you use TypeScriptarrow-up-right, you will probably add a tsconfig.json file. If you use Jestarrow-up-right, your test folder will probably be named __tests__. We also recommend you to add a .editorconfigarrow-up-right file and a .gitignorearrow-up-right file (the latter should include the folder node_modules).

    8. Implement the interface Pluginarrow-up-right. We recommend you to take a look at other plugins that did a similar jobarrow-up-right and encourage you to unit test all your code.

    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 NPMarrow-up-right after making sure that you are using Semantic Versioningarrow-up-right. Congratulations! Tell everybody and ask for feedback! ✌

    any language that "transpiles" to JavaScriptarrow-up-right
    TypeScriptarrow-up-right
    Dartarrow-up-right
    CoffeeScriptarrow-up-right
    npx concordia --plugin-uninstall codeceptjs-webdriverio
    npx concordia --plugin-install codeceptjs-webdriverio
    npx concordia --plugin-uninstall codeceptjs-webdriverio
    npx concordia --plugin-install codeceptjs-playwright
    npx concordia --plugin codeceptjs-playwright --save-config
    npx concordia --db-install mysql
     concordia --plugin-install codeceptjs-webdriverio
    concordia --config /path/to/.concordiarc
    concordia --init
    concordia --save-config --directory ./features
    concordia --directory ./features
    concordia ./features
    concordia --file "file1.feature,/path/to/file2.feature"
    concordia ./feature --file "f1.feature,f2.feature"
    concordia ./feature --ignore "f3.feature,f4.feature"
    concordia ./features --no-recursive
    concordia --dir-result output
    concordia --dir-script test
    concordia --language pt
    concordia --language-list
    concordia --locale-list
    concordia --just-script
    concordia --just-spec
    concordia --just-script
    concordia --just-run
    concordia --no-script
    concordia --no-spec
    concordia --no-test-case
    concordia --no-result
    concordia --no-run
    concordia -x
    concordia --verbose
    concordia --instances 2
    concordia --headless
    concordia --script-file "test1.js,test2.js"
    concordia --script-grep "Sales"
    concordia --target "chrome,firefox,edge"
    concordia --plugin <plugin>
    concordia -p <plugin> -x
    concordia --plugin-info <plugin>
    concordia --plugin-install <plugin>
    concordia --plugin-list
    concordia --plugin-serve <plugin>
    concordia --plugin-uninstall <plugin>
    concordia --db-install <database>
    concordia --db-list
    concordia --db-uninstall <database>
    concordia --random-max-string-size 100
    concordia --random-min-string-size 1
    concordia --random-tries 10
    concordia --seed="example"
    concordia --line-breaker "\n"
    concordia --encoding latin1
    concordia --about
    concordia --help
    concordia --newer
    concordia --version
    npx concordia --db-install mysql
    npx concordia --db-uninstall mysql
    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")' 
        
    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
    """
    npx concordia --report html
    npx concordia --report concordialang-report-html
    {
      "hashes": {
        "feature1.testcase": "ox10JBprHtu5c8822XooloNKUfk=",
        "subdir/feature2.testcase": "DMcj5b67Albe4KhpzyvphC5nVDHn1oCO",
      }
    }
    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
    {
       ...
       
       "concordiaPlugin": {
          "file": "dist/index.js",
          "class": "MyAwesomePlugin",
          "serve": "npx command-to-start-my-testing-server"
       }
    }
    {
        "action": "click",
        "targets": ["#ok"]
    }
    I->click("#ok");
    cy.get('#ok').click();
    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"
    {
        "feature": { "name": "Feature 1" },
        "scenario": { "name": "Scenario 1" },
        "testcases": [
            {
                "scenario": "V1",
                "commands": [
                    {
                        "action": "amOn",
                        "values": [ "https://myapp.com/register" ],
                        "location": { "line": 7, "column": 2 }
                    },
                    {
                        "action": "type",
                        "targets": [ "#email" ],
                        "values": [ "john.doe@example.com" ],
                        "location": { "line": 8, "column": 2 } 
                    },            
                    {
                        "action": "type",
                        "targets": [ "#name" ],
                        "values": [ "John Doe" ],
                        "location": { "line": 9, "column": 4 }                  
                    },                
                    {
                        "action": "click",
                        "targets": [ "#register" ],
                        "location": { "line": 10, "column": 4 } 
                    },
                    {
                        "action": "see",
                        "values": [ "Welcome, John Doe" ],
                        "location": { "line": 11, "column": 2 }
                    }                                         
                ]
            }
        ]
    }
    context("Feature 1", function() {
    
        it("Scenario 1 | V1", function() {
            cy.visit("https://myapp.com/register"); // (7,2)
            cy.get("#email").type("john.doe@example.com"); // (8,2)
            cy.get("#name").type("John Doe"); // (9,4)
            cy.get("#register").click(); // (10,4)
            cy.get("body").should("contain", "Welcome, John Doe"); // (11,2)
        });
    
    });
    npx cypress run --reporter junit
    const scriptLocation = /* extract from stack trace */;
    
    // Suppose that `failureLocation` is:
    // {
    //    path: "tests/feature1.js",
    //    line: 7,
    //    column: 8
    // }
    
    // Retrieve the comment with the reader:
    const specLocation = reader.retrieveSpecLocation(
        scriptLocation
        );
        
    // `specLocation` would be:
    // {
    //    path: "features/feature1.testcase",
    //    line: 10,
    //    column: 4
    // }
    cy.get("#register").click(); // (10,4)
      and I click on <#register>
    npm install --save concordialang-plugin
     ┃
     ┣ src/            🡐 your source code
     ┣ test/           🡐 your tests
     ┣ .editorconfig
     ┣ .gitignore
     ┗ package.json
    cd path/to/your/test-project/
    npm install /path/to/your-plugin
    npm publish
    • 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.

  • 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.

  • lexerarrow-up-right
    parserarrow-up-right
    Natural Language Processingarrow-up-right
    intentarrow-up-right
    semantic analysisarrow-up-right
    Process
    Scenario: Successful login
    Given that I can see the login screen
    When I enter with valid credentials
    Then I can access the application's main screen
    Variant: Login with username and password
    Given that I visit the [Login Screen]
    When I fill {Username}
    And I fill {Password}
    And I click on {OK}
    Then I see "Welcome"
    And I have a ~user logged in~
    Table: Users
    | username | password |
    | bob | 123456 |
    | alice | 4l1c3pass |
    UI Element: Username
    - required
    Otherwise I must see "Please inform the username."
    - value comes from "SELECT username FROM [Users]"
    Otherwise I must see "Invalid username."
    UI Element: Password
    - required
    Otherwise I must see "Please inform the password."
    - value comes from "SELECT password FROM [Users] WHERE username = {Username}"
    Otherwise I must see "Invalid password."
    UI Element: OK
    - type is button
    Constants:
    - "Login Screen" is "/login"
    experimental applicationarrow-up-right
    Remember
    GWTarrow-up-right
    seed
    Language syntaxarrow-up-right
    Available actionsarrow-up-right
    hashtag
    Special line comment
    circle-info
    • Local declaration.

    • At most one declaration per file.

    • Take precedence over a CLI option or configuration file option.

    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 <value> is an available local such as en-US (USA English) or pt-BR (Brazilian Portuguese). NOT AVAILABLE YET.

    Example:

    hashtag
    Tag

    A tag adds metadata to a declaration.

    circle-info
    • 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:

    hashtag
    Special tag

    A special tag changes the compiler's behavior. These are special tags:

    • @scenario( <number> ): references a Scenario by its index, starting at 1.

    • @variant( <number> ): references a Variant by its index, starting at 1.

    • @importance( <number> ): indicates the importance of a declaration. The importance is as high as its number.

    • @generated: indicates that a was computer-generated.

    • @fail: indicates that a should fail.

    • @ignore: whether applied to a , the Variant will not produce Test Cases; whether applied to a it will not produce test scripts.

    • @generate-only-valid-values: avoids that a '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:

      The above example will avoid generating invalid input values (e.g., "A") for testing the format of the Year.

    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> )

    hashtag
    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.

    hashtag
    Feature

    Desired behavior of a software system

    circle-info
    • Global declaration with unique name, at most one declaration per file.

    • Sentences are optional and are not used for generating test cases.

    • Business-oriented sentences, written as a .

    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: User Storyarrow-up-right. 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.

    hashtag
    State

    A certain state of the system

    circle-info
    • Local declaration, only accepted insideVariantsentences.

    • Used for generating test cases.

    A State is denoted by a text between tilde (~), such as ~a state~, and must be written inside Variant sentences. Example:

    A Variant 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 Variant and the ones that can produce it. Since that State is often produced by a Variant from another Feature, you have to import the corresponding Feature file. Concordia Compiler will only search for states from imported Feature files.

    When transforming Variants into Test Cases, 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 Variants that can produce a (same) required State, Concordia Compiler will be able to combine each of them with the current Variant, aiming to produce Test Cases that explore different execution paths. For more information, please read the section Test Coverage and Techniques.

    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 Test Case like the following, that includes sentences from both the features:

    hashtag
    Scenario

    High-level, business-focused, usage scenario of a Feature.

    circle-info
    • Local declaration with a unique name.

    • One or more declarations per Feature.

    • Sentences are optional and are not used for generating test cases.

    • Business-oriented sentences that follow the template .

    Example:

    Template:

    hashtag
    Variant

    Test-oriented declaration that describes a possible interaction between a user and the system in order to accomplish a Scenario.

    circle-info
    • Local declaration, one or more per Scenario.

    • Sentences are used for generating test cases.

    • Test-oriented sentences that follow the template and first-person singular ("I").

    A Variant must use the Given-When-Then template (see Scenario) and first person singular ("I") in its sentences. Concordia Compiler uses Natural Language Processing (NLP)arrow-up-right techniques to understand them.

    circle-exclamation

    See the Actions that you can write in Variant sentences.

    Example:

    Things that can be part of Variant sentences:

    1. References to Constants, such as [New Account Screen]

    2. References to UI Elements, such as {Name} or {New User:Name}.

    3. Widgets, such as <#ok>

    4. , such ~registered user~

    5. Values such as "bob@example.com"

    6. Numbers such as 123

    hashtag
    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.

    • Make sure that your post-conditions are part of the group with Then sentences.

    • Whenever possible, use a single Given , a single When and a single Then sentence. Useand for the other sentences.

    • Write a single action per sentence.

    • Do not use andin the middle of a sentence (break it, instead).

    • Use an additional indentation for and sentences.

    hashtag
    Widgets

    Variant sentences can have widgets (of the user interface) denoted between < and >. Example:

    Concordia adopts the well-known CSS query selectorsarrow-up-right to locate widgets. Plugins convert these selectors to the format adopted by the testing framework.

    Available locators:

    Locator

    Purpose

    Example

    #

    To find a widget by its id.

    When I click on <#save>

    .

    To find a widget by its class.

    When I click on <.btn-primary>

    @

    To find a widget by its name.

    When I click on <@save>

    ~

    circle-check

    Whether you are testing a web application, you may find Katalon-Concordiaarrow-up-right 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.

    hashtag
    Constants

    Declaration block with constant values

    circle-info
    • Global declaration (can be used by other files).

    • At most one declaration per file.

    • Constants cannot have the same name of a Table or a .

    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:

    hashtag
    Table

    Defines a data table that can be used by UI Elements' properties.

    circle-info
    • Global declaration (can be used by other files).

    • Zero, one or many declarations per file.

    • Tables cannot have the same name of a Constant or a .

    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 Constants, their names must be referenced between [ and ]. Example:

    hashtag
    Database

    Declares a database connection that can be used for test data selection

    circle-info
    • Global declaration.

    • Names are shared with Constants and Tables.

    • Allowed more than one declaration per file.

    • Property values must be declared between quotes (").

    Example 1:

    Example 2:

    circle-check

    Database support needs installation of the corresponding drives. See Using Databases for more information on how to install them.

    Properties:

    Property
    Description
    Required

    type

    Database type. Examples: "adodb", "csv", "firebase", "ini", "json", "mysql", "mssql", "postgres", "sqlite", "xlsx". See for all supported types.

    Yes

    host

    URL that indicates where the database is hosted.

    Vary

    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

    hashtag
    UI Element

    hashtag
    Import

    Imports declarations from a .feature file

    circle-info
    • Local declaration.

    • Give access to the declarations from the imported file.

    Examples:

    hashtag
    Test Case

    hashtag
    Test Events

    npx concordia --seed="example" --no-run --no-result
    <TO-DO (UNDER CONSTRUCTION)>
    <TO-DO (UNDER CONSTRUCTION)>
    $ concordia --plugin=codeceptjs --no-test-case --no-script
    # 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)
    });
    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
    # 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~
    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"
    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]
    Table: professions
      | name      | min_salary |
      | Magician  | 100000.00  |
      | 
    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"
    import "file1.feature"
    import "path/to/file2.feature"

    To find a widget by its mobile name.

    When I click on <~save>

    //

    To find a widget by its XPatharrow-up-right.

    When I click on <//form/div/button[1]>

    (none)

    To find a widget by its type.

    When I click on <button>

    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

    Test Case
    Test Case
    Variant
    Test Case
    UI Element
    User Storyarrow-up-right
    Given-When-Thenarrow-up-right
    Given-When-Thenarrow-up-right
    States
    Database
    Database
    Using Databases
      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."