Skip to content

Commit 2184f4b

Browse files
committed
update README and types
1 parent 0299911 commit 2184f4b

File tree

9 files changed

+160
-111
lines changed

9 files changed

+160
-111
lines changed

typescript/README.md

Lines changed: 109 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
[![Workflow](https://github.com/walkframe/covertable/actions/workflows/typescript.yaml/badge.svg)](https://github.com/walkframe/covertable/actions/workflows/typescript.yaml)
33
[![codecov](https://codecov.io/gh/walkframe/covertable/branch/master/graph/badge.svg)](https://codecov.io/gh/walkframe/covertable)
44

5+
# What is covertable?
6+
covertable is a powerful tool for generating pairwise combinations of input factors, designed for both Node.js and browser environments. It's easy to use, flexible, and supports advanced filtering options, making it perfect for testing scenarios and generating comprehensive datasets.
7+
58
# Installation
69

710
```sh
@@ -10,107 +13,101 @@ $ npm install covertable --save
1013

1114
# Usage
1215

13-
## Simple demo in Node.js:
16+
## Simple usage in Node.js:
1417

1518
```javascript
1619
var covertable = require('covertable');
17-
var make = covertable.default;
20+
var make = covertable.make;
1821

19-
var machine = ['iphone', 'pixel'];
20-
var os = ['ios', 'android'];
21-
var browser = ['FireFox', 'Chrome', 'Safari'];
22+
var machine = ["iPhone", "Pixel", "XPERIA", "ZenFone", "Galaxy"];
23+
var os = ["iOS", "Android"];
24+
var browser = ["FireFox", "Chrome", "Safari"];
2225

2326
make([machine, os, browser]);
2427
```
2528
Output:
2629

2730
```javascript
2831
[
29-
[ 'pixel', 'android', 'Chrome' ],
30-
[ 'pixel', 'ios', 'Safari' ],
31-
[ 'pixel', 'android', 'FireFox' ],
32-
[ 'iphone', 'android', 'Safari' ],
33-
[ 'iphone', 'ios', 'Chrome' ],
34-
[ 'iphone', 'ios', 'FireFox' ]
32+
[ 'Pixel', 'iOS', 'Chrome' ],
33+
[ 'ZenFone', 'iOS', 'FireFox' ],
34+
[ 'Pixel', 'Android', 'Safari' ],
35+
[ 'Galaxy', 'Android', 'Chrome' ],
36+
[ 'XPERIA', 'Android', 'FireFox' ],
37+
[ 'Pixel', 'iOS', 'FireFox' ],
38+
[ 'iPhone', 'iOS', 'Safari' ],
39+
[ 'Galaxy', 'iOS', 'Safari' ],
40+
[ 'XPERIA', 'iOS', 'Chrome' ],
41+
[ 'ZenFone', 'Android', 'Chrome' ],
42+
[ 'Galaxy', 'iOS', 'FireFox' ],
43+
[ 'iPhone', 'Android', 'Chrome' ],
44+
[ 'iPhone', 'iOS', 'FireFox' ],
45+
[ 'ZenFone', 'iOS', 'Safari' ],
46+
[ 'XPERIA', 'iOS', 'Safari' ]
3547
]
3648
```
3749

3850
Of course, it also works in the browser well.
3951

40-
## Advanced demo in TypeScript:
52+
## Advanced usage in TypeScript:
4153

42-
```typescript
43-
import { default as make, makeAsync, sorters, criteria } from "covertable";
44-
45-
const machine = ['iphone', 'pixel'];
46-
const os = ['ios', 'android'];
47-
const browser = ['FireFox', 'Chrome', 'Safari'];
48-
49-
make([machine, os, browser], { // optional
50-
length: 2, // default: 2
51-
criterion: criteria.simple, // default: criteria.greedy
52-
sorter: sorters.random, // default: sorters.hash
53-
preFilter: (row: any) => !(row[1] === 'android' && row[0] !== 'pixel'), // default: null
54-
postFilter: (row: any) => !(row[1] === 'ios' && row[2] !== 'Safari'), // default: null
55-
});
56-
```
54+
As previously mentioned, when elements are specified as an array, the results will also be in array form. However, if the elements are specified as an object, the results will be in object form.
5755

58-
Output:
56+
The following example uses preFilter and postFilter to apply constraints to the output results. In this case, `SuggestRowType` can also be used to infer the type of row parameters that the filter function receives.
5957

6058
```typescript
61-
[ // filtered
62-
[ 'iphone', 'ios', 'Safari' ],
63-
[ 'pixel', 'android', 'Chrome' ],
64-
[ 'pixel', 'ios', 'Safari' ]
65-
]
66-
```
59+
import { make, sorters, criteria, SuggestRowType, DictType } from "covertable";
6760

68-
You can use also `makeAsync` function (generator).
69-
- It receives the same arguments with `make` function.
70-
- It returns the row at the time it's made.
61+
const machine = ["iPhone", "Pixel", "XPERIA", "ZenFone", "Galaxy"];
62+
const os = ["iOS", "Android"];
63+
const browser = ["FireFox", "Chrome", "Safari"];
7164

72-
## Object input and output
65+
const factors = {machine, os, browser};
7366

74-
You can specify `factors` as object type:
75-
76-
```typescript
77-
import { default as make, sorters, criteria } from "covertable";
78-
79-
const machine = ['iphone', 'pixel'];
80-
const os = ['ios', 'android'];
81-
const browser = ['FireFox', 'Chrome', 'Safari'];
82-
83-
make({machine, os, browser}, { // optional
67+
make(factors, { // optional
8468
length: 2,
85-
preFilter: (row: any) => !(row.os === 'android' && row.machine !== 'pixel'), // default: null
86-
postFilter: (row: any) => !(row.os === 'ios' && row.browser !== 'Safari'), // default: null
69+
// SuggestRowType<typeof factors> is { machine: string, os: string, browser: string }
70+
preFilter: (row: SuggestRowType<typeof factors>) => !(row.os === 'Android' && row.machine !== 'Pixel'), // default: null
71+
// Or DictType that is { [key: string]: string }
72+
postFilter: (row: DictType) => !(row.os === 'iOS' && row.browser !== 'Safari'), // default: null
8773
});
8874
```
8975

9076
Then the output will change as follows:
9177

9278
```typescript
9379
[ // filtered
94-
{ machine: 'iphone', browser: 'Safari', os: 'ios' },
95-
{ machine: 'pixel', browser: 'Chrome', os: 'android' },
96-
{ machine: 'pixel', browser: 'Safari', os: 'ios' },
80+
{ machine: 'Pixel', os: 'Android', browser: 'FireFox' },
81+
{ machine: 'iPhone', os: 'iOS', browser: 'Safari' },
82+
{ machine: 'Galaxy', browser: 'Safari', os: 'iOS' },
83+
{ machine: 'Pixel', browser: 'Safari', os: 'iOS' },
84+
{ machine: 'ZenFone', browser: 'Safari', os: 'iOS' },
85+
{ machine: 'XPERIA', browser: 'Safari', os: 'iOS' }
9786
]
9887
```
9988

100-
## Options
101-
`covertable.make` function has options as `object` at 2nd argument.
89+
You can use also `makeAsync` function (generator).
90+
- It receives the same arguments with `make` function.
91+
- It returns the row at the time it's made.
10292

103-
All options are omittable.
93+
```js
94+
import { makeAsync } from "covertable";
95+
96+
for await (const row of makeAsync([machine, os, browser])) {
97+
console.log(row);
98+
}
99+
```
100+
101+
## Options
102+
The `covertable.make` function accepts an options object as its second argument. Here are the available options:
104103

105104
### length
106105
Number of factors to be covered. (default: 2)
107106

108107
Obviously the more it increases, the more number of combinations increases.
109108

110109
### sorter
111-
Combinations depend on the order of spreading all over the rows.
112-
113-
You can choice a sorter from the following:
110+
Determines the order of combinations.
114111

115112
- sorters.random: It makes different combinations everytime. (fastest)
116113
- sorters.hash: It makes combinations depending on hash of the pair and seed. (default)
@@ -120,7 +117,7 @@ You can choice a sorter from the following:
120117
- When the combination of factors and seed are the same, covertable reproduces the same collective.
121118

122119
### criterion
123-
You can choice a criterion from the following:
120+
Determines the efficiency of combinations.
124121

125122
- `criteria.simple`: it extracts any pairs that can be stored into the processing row.
126123
- `criteria.greedy`: it attempts to make most efficient combinations. (default)
@@ -132,23 +129,69 @@ Although the latter is superior to former in terms of fewer combinations general
132129
Not relevant options will be ignored.
133130

134131
### preFilter
135-
This is a function to filter beforehand.
136-
137-
It receives an argument `row` as `object` type.
132+
Function to filter combinations before they are registered.
138133

139134
When the function returns `false`, the row combination will not be registered.
140135
- If factors type is `Array`, you should specify an index at the subscript like `row => row[1] < 6`.
141136
- If factors type is `Object`, you should specify a key at the subscript like `row => row.month < 6` or `row => row['month'] < 6`
142137

143138
### postFilter
144-
This means a function to filter later.
139+
Function to filter combinations after they are generated.
145140

146141
The usage is the same as `preFilter`, only the difference is the timing of the call.
147142
It will delete rows not matched this function at the last.
148143

149144
For this reason, the final test cases may not satisfy the factors coverage.
150145

151-
# Requirement
146+
### PictConstraintsLexer
147+
148+
Filter functions can also be generated using PictConstraintsLexer. Use as follows
149+
This function is supported only in the typescript version.
150+
151+
```js
152+
import { make, PictConstraintsLexer } from "covertable";
153+
154+
const machine = ["iPhone", "Pixel", "XPERIA", "ZenFone", "Galaxy"];
155+
const os = ["iOS", "Android"];
156+
const browser = ["FireFox", "Chrome", "Safari"];
157+
158+
const lexer = new PictConstraintsLexer(
159+
`
160+
IF [machine] = "iPhone" THEN [os] = "iOS";
161+
IF [os] = "iOS" THEN [machine] = "iPhone";
162+
`, true
163+
);
164+
165+
make({machine, os, browser}, { // optional
166+
preFilter: lexer.filter,
167+
});
168+
```
169+
170+
```js
171+
[
172+
{ machine: 'ZenFone', browser: 'FireFox', os: 'Android' },
173+
{ os: 'Android', browser: 'Safari', machine: 'Pixel' },
174+
{ machine: 'Galaxy', browser: 'Chrome', os: 'Android' },
175+
{ machine: 'XPERIA', os: 'Android', browser: 'FireFox' },
176+
{ machine: 'Pixel', browser: 'Chrome', os: 'Android' },
177+
{ os: 'iOS', browser: 'FireFox', machine: 'iPhone' },
178+
{ machine: 'Pixel', browser: 'FireFox', os: 'Android' },
179+
{ os: 'iOS', browser: 'Chrome', machine: 'iPhone' },
180+
{ machine: 'Galaxy', browser: 'Safari', os: 'Android' },
181+
{ machine: 'ZenFone', browser: 'Chrome', os: 'Android' },
182+
{ os: 'iOS', browser: 'Safari', machine: 'iPhone' },
183+
{ machine: 'Galaxy', browser: 'FireFox', os: 'Android' },
184+
{ machine: 'XPERIA', browser: 'Chrome', os: 'Android' },
185+
{ machine: 'ZenFone', browser: 'Safari', os: 'Android' },
186+
{ machine: 'XPERIA', browser: 'Safari', os: 'Android' }
187+
]
188+
```
189+
190+
This feature acts as a conversion tool that enables the implementation of PICT constraint conditions within CoverTable,
191+
allowing users to seamlessly apply complex constraints to their test data generation.
192+
193+
194+
# Requirements
152195

153196
ES2015 or later
154197

@@ -179,6 +222,6 @@ $ npm version patch
179222
$ npm publish
180223
```
181224

182-
# More info
225+
# More information
183226

184227
- [walkframe/covertable - GitHub](https://github.com/walkframe/covertable)

typescript/src/__tests__/filter.test.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
1-
import { Dict, SuggestRowType } from "../types";
2-
import { make, makeAsync, sorters, criteria } from "../index";
1+
import { make, DictType, SuggestRowType } from "../";
32

43
const machine = ["iPhone", "Pixel", "XPERIA", "ZenFone", "Galaxy"];
54
const os = ["iOS", "Android"];
65
const browser = ["FireFox", "Chrome", "Safari"];
76

87
test('exclude impossible combinations', () => {
98
const factors = {machine, os, browser};
10-
const preFilter = (row: Dict) => {
9+
const preFilter = (row: DictType) => {
1110
return !(
1211
(row.machine === 'iPhone' && row.os !== 'iOS') ||
1312
(row.machine !== 'iPhone' && row.os === 'iOS')
@@ -49,14 +48,14 @@ test('Limited to iphone and iOS combinations only.', () => {
4948

5049
test('Use a constant-false function for preFilter', () => {
5150
const factors = {machine, os, browser};
52-
const preFilter = (row: Dict) => false;
51+
const preFilter = (row: DictType) => false;
5352
const rows = make(factors, { preFilter });
5453
expect(rows).toEqual([]);
5554
});
5655

5756
test('Use the wrong conditional function for preFilter', () => {
5857
const factors = {machine, os, browser};
59-
const preFilter = (row: Dict) => row.machine === 'WindowsPhone';
58+
const preFilter = (row: DictType) => row.machine === 'WindowsPhone';
6059
const rows = make(factors, { preFilter });
6160
expect(rows).toEqual([]);
6261
});

typescript/src/__tests__/index.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { default as make, sorters, criteria } from '../index';
22
import { product, combinations, range, len, all, getItems } from '../lib';
3-
import { FactorsType, Scalar, Dict, PairType } from '../types';
3+
import { FactorsType, ScalarType, DictType, PairType } from '../types';
44

55
const getPairs = function* (factors: FactorsType, length = 2) {
66
const allKeys = getItems(factors).map(([k, _]) => k);
@@ -69,7 +69,7 @@ test('prefilter excludes specified pairs before', () => {
6969
["d", "e"],
7070
["f"],
7171
];
72-
const preFilter = (row: Dict) => {
72+
const preFilter = (row: DictType) => {
7373
if (row[0] === "a" && row[1] === "d") {
7474
return false;
7575
}
@@ -151,7 +151,7 @@ test('dict type factors make dict row', () => {
151151
'key5': ["m", "n", "o"],
152152
};
153153
const rows = make(factors);
154-
const sorter = (a: Scalar, b: Scalar) => a > b ? 1 : -1;
154+
const sorter = (a: ScalarType, b: ScalarType) => a > b ? 1 : -1;
155155
for (let row of rows) {
156156
const keys1 = Object.keys(row).sort(sorter);
157157
const keys2 = Object.keys(factors).sort(sorter);

typescript/src/controller.ts

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@ import {
1717
IndicesType,
1818
FactorsType,
1919
SerialsType,
20-
Scalar,
21-
Dict,
22-
PairByKey,
20+
ScalarType,
21+
DictType,
22+
PairByKeyType,
2323
ParentsType,
2424
CandidateType,
2525
RowType,
@@ -29,9 +29,9 @@ import {
2929
} from "./types";
3030
import { NeverMatch, NotReady } from "./exceptions";
3131

32-
export class Row extends Map<Scalar, number> implements RowType {
32+
export class Row extends Map<ScalarType, number> implements RowType {
3333
// index: number
34-
public consumed: PairByKey = new Map();
34+
public consumed: PairByKeyType = new Map();
3535

3636
constructor(row: CandidateType) {
3737
super();
@@ -57,9 +57,9 @@ export class Controller<T extends FactorsType> {
5757
private serials: SerialsType = new Map();
5858
private parents: ParentsType = new Map();
5959
private indices: IndicesType = new Map();
60-
public incomplete: PairByKey = new Map();
60+
public incomplete: PairByKeyType = new Map();
6161

62-
private rejected: Set<Scalar> = new Set();
62+
private rejected: Set<ScalarType> = new Set();
6363
public row: Row;
6464

6565
constructor(public factors: FactorsType, public options: OptionsType<T>) {
@@ -169,8 +169,8 @@ export class Controller<T extends FactorsType> {
169169
return row.size === this.factorLength;
170170
}
171171

172-
toMap(row: Row): Map<Scalar, number[]> {
173-
const result: Map<Scalar, number[]> = new Map();
172+
toMap(row: Row): Map<ScalarType, number[]> {
173+
const result: Map<ScalarType, number[]> = new Map();
174174
for (let [key, serial] of row.entries()) {
175175
const index = this.indices.get(serial) as number;
176176
const first = this.indices.get((this.serials.get(key) as PairType)[0]);
@@ -181,15 +181,15 @@ export class Controller<T extends FactorsType> {
181181
}
182182

183183
toProxy(row: Row) {
184-
const obj: Dict = {};
184+
const obj: DictType = {};
185185
for (let [key, value] of this.toMap(row).entries()) {
186186
obj[key] = value;
187187
}
188188
return new Proxy(obj, proxyHandler) as SuggestRowType<T>;
189189
}
190190

191191
toObject(row: Row) {
192-
const obj: Dict = {};
192+
const obj: DictType = {};
193193
for (let [key, value] of this.toMap(row).entries()) {
194194
obj[key] = value;
195195
}

typescript/src/criteria/greedy.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
import type {FactorsType, PairByKey, PairType} from '../types';
1+
import type {FactorsType, PairByKeyType, PairType} from '../types';
22
import { combinations, unique } from '../lib';
33
import { Controller } from '../controller';
44

5-
const getNumRemovablePairs = (indexes: Set<number>, incomplete: PairByKey, length: number) => {
5+
const getNumRemovablePairs = (indexes: Set<number>, incomplete: PairByKeyType, length: number) => {
66
let num = 0;
77
const removingKeys = combinations([... indexes], length);
88
for (let pair of removingKeys) {

0 commit comments

Comments
 (0)