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 become2not"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;
}