Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ test.describe('7. Test Data Generation', () => {

const schemaText = await appPage.testDataPanel.getSchemaText();
expect(schemaText).toContain('New Column');
expect(schemaText).toContain('literal()');
expect(schemaText).toContain('literal("")');
expectNoPageErrors(pageErrors);
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ tags: [release, architecture, test-data, faker, domain]
date: 2026-05-18T10:30
---

As AnyWayData usage grew across UI, CLI, API, and MCP, we found that directly exposing raw faker calls created avoidable fragility.
As AnyWayData usage grew across UI, CLI, API, and MCP, we found that directly exposing raw faker calls created avoidable fragility.

We moved to a **domain abstraction layer** so schemas describe intent (`internet.email`, `number.int`, `date.recent`) instead of vendor-specific call shapes.

<!-- truncate -->
Expand Down
71 changes: 71 additions & 0 deletions docs-src/blog/2026-05-19-string-counterstring-domain-method.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
---
slug: string-counterstring-domain-method
title: "Added string.counterString to Domain Test Data"
authors: [alan]
tags: [release, test-data, domain]
---

`string.counterString` is now available in the domain model.

This is the first `string.*` domain addition that is not backed directly by Faker, and is implemented as a custom domain delegate.

<!-- truncate -->

## Why add it?

Counterstrings are useful for field-length and truncation testing because each marker shows its position in the generated text.

Example pattern:

`*3*5*7*9*12*15*`

If we entered the above into a field that truncated to 14 we would see:

`*3*5*7*9*12*15` - this looks incorrect because I'm expecting to see a number before an `*`, if I see a number without an `*` then I know there has been some truncation.

## Usage

`string.counterString(min, max, delimiter="*")`

- If only one integer is supplied, it is used as both min and max.
- If two integers are supplied, the lower value is used as min and the higher as max.
- Lowest allowed min is `1`.
- Delimiter defaults to `*`.
- If a delimiter longer than one character is passed, only the first character is used.

Examples:

```txt
Fixed15
string.counterString(15)
```

```txt
Range5to12
string.counterString(min=5, max=12)
```

```txt
HashDelimited
string.counterString(12, 12, "#")
```

## Take Care

We've added no limits to the length of the counterstrings to take care in your definitions. Sure you can create 1,000,000 rows with counterstrings that are 1,000,000 characters long, but I'm not sure if your computer is going to like generating that.

We default to a `1-25` character counterstring to mitigate this risk somewhat.

## Other counterstring resources

If you are interested in using CounterStrings in your testing then checkout this Chrome extension:

- https://www.eviltester.com/page/tools/counterstringjs/

It allows you to generate counterstrings and other data directly into the browser fields.

Also more information about CounterStrings here:

- https://www.satisfice.com/blog/archives/22
- https://www.eviltester.com/blog/eviltester/news/counterstring-new-version-dec-2025/
- https://www.eviltester.com/categories/counterstrings/
1 change: 1 addition & 0 deletions docs-src/docs/040-test-data/010-test-data-generation.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,5 +45,6 @@ Both workflows support generation rules such as:
- [Literal Data](/docs/test-data/literal-test-data)
- [Regex Based Data](/docs/test-data/regex-test-data)
- [Faker Based Data](/docs/test-data/faker-test-data)
- [Counterstrings](/docs/test-data/counterstrings)
- [Domain Test Data](/docs/test-data/domain/domain-test-data)
- [All Pairs Combinatorial Testing](/docs/test-data/pairwise-testing) - Generate optimal test combinations from enum data
67 changes: 67 additions & 0 deletions docs-src/docs/040-test-data/030-counterstrings.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
---
sidebar_position: 30
title: "Counterstrings"
description: "Use counterstrings to test length limits, truncation, and boundary handling."
---

A counterstring is a specially structured string where marker numbers show exact character positions.

Example of a 15-character counterstring:

```txt
*3*5*7*9*12*15*
```

Each `*` is preceded by the numeric position in the string. So the final `*` is at position 15.

This makes it easy to see where text is cut off, wrapped, or rejected.

## Why Use Counterstrings

Counterstrings are useful for:

- field length boundary testing
- truncation behavior checks
- UI clipping and overflow diagnostics
- API validation and error-message checks

## AnyWayData Support

AnyWayData supports counterstrings through:

```txt
string.counterString(min, max, delimiter="*")
```

Behavior:

- one integer: fixed length (`min == max`)
- two integers: range (smaller becomes min, larger becomes max)
- minimum effective length is `1`
- delimiter defaults to `*`
- multi-character delimiter uses first character

Examples:

```txt
string.counterString(15)
```

```txt
string.counterString(5, 12)
```

```txt
string.counterString(12, 12, "#")
```

## Related Links

- [string domain reference](/docs/test-data/domain/string)
- [Domain Test Data overview](/docs/test-data/domain/domain-test-data)
- [Counterstring reference implementation](https://github.com/eviltester/counterstringjs/blob/master/extension/js/counterstring.js)
- [James Bach on counterstrings](https://www.satisfice.com/blog/archives/22)
- [CounterString Chrome Extension](https://www.eviltester.com/page/tools/counterstringjs/)
- [Alan Richardson on counterstrings](https://www.eviltester.com/categories/counterstrings/)


5 changes: 5 additions & 0 deletions docs-src/docs/040-test-data/domain/000-domain-test-data.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@ Num
number.int(min=32, max=47)
```

## Migration

- Before (invalid): `domain.helpers.fake("...")`
- After: `faker.helpers.fake("...")` (or `helpers.fake("...")` in faker contexts)

For faker helper templates and utility functions, use faker helpers:
- [Faker Helpers](/docs/test-data/faker/helpers)
- [fakerjs.dev/api/helpers](https://fakerjs.dev/api/helpers)
Expand Down
38 changes: 13 additions & 25 deletions docs-src/docs/040-test-data/domain/020-airline.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ airline.aircraftType()
```

Example return values:
- `"narrowbody"`
- `regional`

### `airline.airline`

Expand All @@ -48,8 +48,7 @@ airline.airline()
```

Example return values:
- `{"name":"Air China","iataCode":"CA"}`
- `{"name":"TUI Airways","iataCode":"BY"}`
- `{"name":"American Airlines","iataCode":"AA"}`

### `airline.airline.iataCode`

Expand All @@ -67,8 +66,7 @@ airline.airline.iataCode()
```

Example return values:
- `"PK"`
- `"MN"`
- `AA`

### `airline.airline.name`

Expand All @@ -86,8 +84,7 @@ airline.airline.name()
```

Example return values:
- `"Tunisair"`
- `"SunExpress"`
- `Acme Air`

### `airline.airplane`

Expand All @@ -105,8 +102,7 @@ airline.airplane()
```

Example return values:
- `{"name":"Airbus A380-800","iataTypeCode":"388"}`
- `{"name":"Boeing 747-400","iataTypeCode":"744"}`
- `{"name":"Airbus A320","iataTypeCode":"A320"}`

### `airline.airplane.iataTypeCode`

Expand All @@ -124,8 +120,7 @@ airline.airplane.iataTypeCode()
```

Example return values:
- `"M81"`
- `"732"`
- `A320`

### `airline.airplane.name`

Expand All @@ -143,8 +138,7 @@ airline.airplane.name()
```

Example return values:
- `"Boeing 737-200"`
- `"Douglas DC-10"`
- `Boeing 737`

### `airline.airport`

Expand All @@ -162,8 +156,7 @@ airline.airport()
```

Example return values:
- `{"name":"Noumea Magenta Airport","iataCode":"GEA"}`
- `{"name":"Melbourne International Airport","iataCode":"MEL"}`
- `{"name":"Heathrow Airport","iataCode":"LHR"}`

### `airline.airport.iataCode`

Expand All @@ -181,8 +174,7 @@ airline.airport.iataCode()
```

Example return values:
- `"HBA"`
- `"AKL"`
- `LHR`

### `airline.airport.name`

Expand All @@ -200,8 +192,7 @@ airline.airport.name()
```

Example return values:
- `"Chicago O'Hare International Airport"`
- `"Murtala Muhammed International Airport"`
- `London Heathrow Airport`

### `airline.flightNumber`

Expand All @@ -219,8 +210,7 @@ airline.flightNumber()
```

Example return values:
- `"99"`
- `"15"`
- `1`

### `airline.recordLocator`

Expand All @@ -238,8 +228,7 @@ airline.recordLocator()
```

Example return values:
- `"HSRWTV"`
- `"BKENFS"`
- `TCSJCN`

### `airline.seat`

Expand All @@ -263,5 +252,4 @@ airline.seat(aircraftType="narrowbody")
```

Example return values:
- `"23D"`
- `"9E"`
- `17F`
Loading