Data Tables
Autometa sports a more sophisticated data table model than the default
Cucumber.js
implementation. Specifically, there are multiple table types
which can be configured on a per step basis.
When a step contains a table, it will be passed to the matching definition callback according to the following rules:
- If the Gherkin step contains a table, and none is defined in the matching definition an error will be thrown
- If no expressions exist, then the Data Table will be injected as the first argument in the definition callback
- If at least one expression exists, the Data Table will be the next argument passed after all expressions
The currently supported table types are:
HTable
A standard 'horizontal' table, where the first row of the table is treated as a header cell, and values can be accessed by indexing against that header
- Gherkin Datatable
- Step Definition
Given a step with a table
| Username | Is Admin |
| Bob | true |
| Jill | false |
Given(
"a step with a table",
(table) => {
const bob = table.get<string>("Username", 0);
const bobIsAdmin = table.get<number>("Is Admin", 0);
const allUsers = table.get<string[]>("Username");
},
HTable
);
VTable
A standard 'vertical' whose header cells are stacked vertically on the leftmost column
- Gherkin Datatable
- Step Definition
Given a step with a table
| Username | Bob | Jill |
| Is Admin | true | false |
Given(
"a step with a table",
(table) => {
const bob = table.get<string>("Username", 0);
const bobIsAdmin = table.get<number>("Is Admin", 0);
const allUsers = table.get<string[]>("Username");
},
VTable
);
MTable
A Matrix tyle table, where the first row and first column are both treated as headers, indexing against both will return the value of a single cell.
- Gherkin Datatable
- Step Definition
Given a step with a table
| | Big | Small |
| Blue | Ocean | Puddle |
| Green | Ireland | Cabbage |
Given(
"a step with a table",
(table) => {
const ocean = table.get<string>("Blue", "Big");
const cabbage = table.get<string>("Green", "Small");
},
MTable
);
By default, tables will attempt to convert their cell values into a primitive type.
I.e. | 2 |
will attempt to parse this cell as a number, true
will become a bool etc.
To access the unconverted raw value, pass true to the end of the .get
method
Given("step with table", (table) => {
const firstUsername = table.get<string>("Username", 0);
const firstAge = table.get<string>("Age", 0, true);
// ...
});
Custom Tables
It is possible to implement your own table. Simply create a class which accepts as a constructor parameter and instance of CompiledDataTable.
The compiled table contains two 2d arrays:
table
- List of converted types. I.e
| 2 |
will become2
not"2"
- List of converted types. I.e
rawTable
- List of unconverted types. I.e
| 2 |
will remain"2"
not2
- List of unconverted types. I.e
import { CompiledDataTable } from "@autometa/runner";
export class MyTable {
constructor(private readonly compiledTable: CompiledDataTable) {}
get<T>(header: string, row: number, raw = false): T {
// Your get logic here
}
}
There is no common interface for what methods can be on a table, so you will need to decide your own.
Table Documents
Table documents are a way of automatically mapping a table to a js object
with named properties. Only vertical (VTable
) and horizontal (HTable
) tables
are supported for table documents.
Defining Table Documents
To declare a table document, you can create a class which extends from
the return value of the static .Document
method which exists on
HTable
and VTable
.
Properties can be defined by using the respective tables static
.cell
method, which accepts a title for the row or column which defines
the document.
import { HTable } from "@autometa/runner";
class UserTable extends HTable.Document() {
@HTable.cell("Username")
username: string;
@HTable.cell("Is Admin")
isAdmin: boolean;
}
// VTable
import { VTable } from "@autometa/runner";
class UserTable extends VTable.Document() {
@VTable.cell("Username")
username: string;
@VTable.cell("Is Admin")
isAdmin: boolean;
}
Using Table Documents
If a table type blueprint (uninstantiated class reference) passed to a Step Definition is a TableDocument, then the table will automatically be converted to an array of TableDocument instances. This array will be provided to the Step Definition callback where a table would normally be if defined.
// user.document.ts
import { HTable } from "@autometa/runner";
class UserTable extends HTable.Document() {
@HTable.cell("Username")
username: string;
@HTable.cell("Is Admin")
isAdmin: boolean;
}
// user.given.ts
Given(
"a step with a table",
(table) => {
const firstUser = table[0];
const firstUsername = firstUser.username;
const firstIsAdmin = firstUser.isAdmin;
},
UserTable
);
As documents are provided as an array, loops. maps, filters and destructuring will all work as expected.
Transformers
Transformers can be used to convert the raw value of a cell into a different type. This can be useful for converting strings into numbers, dates etc.
import { HTable } from "@autometa/runner";
class UserTable extends HTable.Document() {
@HTable.cell("Username")
username: string;
@HTable.cell("Date Of Birth", (dob) => new Date(dob))
age: Date;
}