@interactors/core 1.0.0-rc1.2 → 1.0.0-rc1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -0
- package/dist/cjs/constructor.js +62 -126
- package/dist/cjs/constructor.js.map +1 -1
- package/dist/cjs/converge.js +3 -5
- package/dist/cjs/converge.js.map +1 -1
- package/dist/cjs/format.js +50 -0
- package/dist/cjs/format.js.map +1 -0
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/inspector.js +2 -1
- package/dist/cjs/inspector.js.map +1 -1
- package/dist/cjs/interaction.js +59 -35
- package/dist/cjs/interaction.js.map +1 -1
- package/dist/cjs/match.js +17 -7
- package/dist/cjs/match.js.map +1 -1
- package/dist/cjs/resolvers.js +98 -0
- package/dist/cjs/resolvers.js.map +1 -0
- package/dist/cjs/serialize.js +48 -0
- package/dist/cjs/serialize.js.map +1 -0
- package/dist/constructor.d.ts +0 -6
- package/dist/constructor.d.ts.map +1 -1
- package/dist/converge.d.ts +2 -1
- package/dist/converge.d.ts.map +1 -1
- package/dist/esm/constructor.js +59 -117
- package/dist/esm/constructor.js.map +1 -1
- package/dist/esm/converge.js +3 -5
- package/dist/esm/converge.js.map +1 -1
- package/dist/esm/{format-table.js → format.js} +19 -1
- package/dist/esm/format.js.map +1 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/inspector.js +2 -1
- package/dist/esm/inspector.js.map +1 -1
- package/dist/esm/interaction.js +56 -29
- package/dist/esm/interaction.js.map +1 -1
- package/dist/esm/match.js +17 -7
- package/dist/esm/match.js.map +1 -1
- package/dist/esm/resolvers.js +88 -0
- package/dist/esm/resolvers.js.map +1 -0
- package/dist/esm/serialize.js +43 -0
- package/dist/esm/serialize.js.map +1 -0
- package/dist/format.d.ts +10 -0
- package/dist/format.d.ts.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/inspector.d.ts.map +1 -1
- package/dist/interaction.d.ts +38 -10
- package/dist/interaction.d.ts.map +1 -1
- package/dist/match.d.ts +5 -5
- package/dist/match.d.ts.map +1 -1
- package/dist/resolvers.d.ts +9 -0
- package/dist/resolvers.d.ts.map +1 -0
- package/dist/serialize.d.ts +13 -0
- package/dist/serialize.d.ts.map +1 -0
- package/dist/specification.d.ts +14 -11
- package/dist/specification.d.ts.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +3 -2
- package/src/constructor.ts +65 -133
- package/src/converge.ts +3 -6
- package/src/format.ts +58 -0
- package/src/index.ts +1 -1
- package/src/inspector.ts +2 -1
- package/src/interaction.ts +101 -36
- package/src/match.ts +23 -15
- package/src/resolvers.ts +100 -0
- package/src/serialize.ts +54 -0
- package/src/specification.ts +16 -13
- package/dist/cjs/format-table.js +0 -30
- package/dist/cjs/format-table.js.map +0 -1
- package/dist/esm/format-table.js.map +0 -1
- package/dist/format-table.d.ts +0 -6
- package/dist/format-table.d.ts.map +0 -1
- package/src/format-table.ts +0 -34
package/src/resolvers.ts
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
|
|
2
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
3
|
+
|
|
4
|
+
import { globals } from '@interactors/globals';
|
|
5
|
+
import { Match } from './match';
|
|
6
|
+
import { NoSuchElementError, NotAbsentError, AmbiguousElementError } from './errors';
|
|
7
|
+
import { formatDescription, formatMatchesTable } from './format';
|
|
8
|
+
import { InteractorOptions } from './specification';
|
|
9
|
+
|
|
10
|
+
const defaultSelector = 'div';
|
|
11
|
+
|
|
12
|
+
export function findElements<E extends Element>(parentElement: Element, interactor: InteractorOptions<any, any, any>): E[] {
|
|
13
|
+
if (!interactor.name) {
|
|
14
|
+
throw new Error('One of your interactors was created without a name. Please provide a label for your interactor:\n\tHTML.extend(\'my interactor\') || createInteractor(\'my interactor\')');
|
|
15
|
+
}
|
|
16
|
+
if (typeof interactor.specification.selector === 'function') {
|
|
17
|
+
return interactor.specification.selector(parentElement);
|
|
18
|
+
} else if (interactor.specification.selector === ':root') {
|
|
19
|
+
// this is a bit of a hack, because otherwise there isn't a good way of selecting the root element
|
|
20
|
+
return [parentElement.ownerDocument.querySelector(':root') as E];
|
|
21
|
+
} else {
|
|
22
|
+
return Array.from(parentElement.querySelectorAll(interactor.specification.selector || defaultSelector));
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function findMatches(parentElement: Element, interactor: InteractorOptions<any, any, any>): Match<Element, any>[] {
|
|
27
|
+
return findElements(parentElement, interactor).map((e) => new Match(e, interactor.filter, interactor.locator));
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function throwNoSuchElementError(matches: Match<Element, any>[], interactor: InteractorOptions<any, any, any>): never {
|
|
31
|
+
if (matches.length === 0) {
|
|
32
|
+
throw new NoSuchElementError(`did not find ${formatDescription(interactor)}`);
|
|
33
|
+
} else {
|
|
34
|
+
throw new NoSuchElementError(`did not find ${formatDescription(interactor)}, did you mean one of:\n\n${formatMatchesTable(interactor, matches)}`);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function findFirstMatch(parentElement: Element, interactor: InteractorOptions<any, any, any>): Match<Element, any> {
|
|
39
|
+
let matches: Match<Element, any>[] = []
|
|
40
|
+
for (let element of findElements(parentElement, interactor)) {
|
|
41
|
+
let match = new Match(element, interactor.filter, interactor.locator)
|
|
42
|
+
matches.push(match)
|
|
43
|
+
if (match.matches) {
|
|
44
|
+
return match
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
throwNoSuchElementError(matches, interactor);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function hasMatchMatching(parentElement: Element, interactor: InteractorOptions<any, any, any>): boolean {
|
|
51
|
+
return findElements(parentElement, interactor).some(element => new Match(element, interactor.filter, interactor.locator).matches);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function findMatchesNonEmpty(parentElement: Element, interactor: InteractorOptions<any, any, any>): Match<Element, any>[] {
|
|
55
|
+
let matches = findMatches(parentElement, interactor);
|
|
56
|
+
let matching = matches.filter((m) => m.matches);
|
|
57
|
+
if (matching.length > 0) {
|
|
58
|
+
return matching;
|
|
59
|
+
}
|
|
60
|
+
throwNoSuchElementError(matches, interactor);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Given a parent element, and an interactor, find exactly one matching element
|
|
64
|
+
// and return it. If no elements match, raise an error. If more than one
|
|
65
|
+
// element matches, raise an error.
|
|
66
|
+
export function resolveUnique(parentElement: Element, interactor: InteractorOptions<any, any, any>): Element {
|
|
67
|
+
let matching = findMatchesNonEmpty(parentElement, interactor);
|
|
68
|
+
|
|
69
|
+
if (matching.length === 1) {
|
|
70
|
+
return matching[0].element;
|
|
71
|
+
} else {
|
|
72
|
+
let alternatives = matching.map((m) => '- ' + m.elementDescription());
|
|
73
|
+
throw new AmbiguousElementError(`${formatDescription(interactor)} matches multiple elements:\n\n${alternatives.join('\n')}`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Given a parent element, and an interactor, find the first matching element and
|
|
78
|
+
// return it. If no elements match, raise an error.
|
|
79
|
+
export function resolveFirst(parentElement: Element, interactor: InteractorOptions<any, any, any>): Element {
|
|
80
|
+
return findFirstMatch(parentElement, interactor).element
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Given a parent element, and an interactor, check if there are any matching
|
|
84
|
+
// elements, and throw an error if there are. Otherwise return undefined.
|
|
85
|
+
export function resolveEmpty(parentElement: Element, interactor: InteractorOptions<any, any, any>): void {
|
|
86
|
+
let matching = findMatches(parentElement, interactor).filter((m) => m.matches)
|
|
87
|
+
|
|
88
|
+
if (matching.length !== 0) {
|
|
89
|
+
let alternatives = matching.map((m) => '- ' + m.elementDescription());
|
|
90
|
+
throw new NotAbsentError(`${formatDescription(interactor)} exists but should not:\n\n${alternatives.join('\n')}`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export function unsafeSyncResolveParent(options: InteractorOptions<any, any, any>): Element {
|
|
95
|
+
return options.ancestors.reduce(resolveUnique, globals.document.documentElement);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export function unsafeSyncResolveUnique<E extends Element>(options: InteractorOptions<E, any, any>): E {
|
|
99
|
+
return resolveUnique(unsafeSyncResolveParent(options), options) as E;
|
|
100
|
+
}
|
package/src/serialize.ts
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
import { InteractionOptions as SerializedInteractionOptions, InteractionType } from "@interactors/globals";
|
|
3
|
+
import { pascalCase } from "change-case";
|
|
4
|
+
import { InteractionOptions } from "./interaction";
|
|
5
|
+
import { matcherCode } from "./matcher";
|
|
6
|
+
import { InteractorOptions } from "./specification";
|
|
7
|
+
|
|
8
|
+
export function serializeInteractorOptions(options: InteractorOptions<any, any, any>): {
|
|
9
|
+
interactor: string;
|
|
10
|
+
filter: { [name: string]: unknown };
|
|
11
|
+
locator: string;
|
|
12
|
+
code: () => string;
|
|
13
|
+
} {
|
|
14
|
+
let locator = matcherCode(options.locator?.value);
|
|
15
|
+
return {
|
|
16
|
+
interactor: options.name,
|
|
17
|
+
filter: options.filter.all,
|
|
18
|
+
locator,
|
|
19
|
+
code() {
|
|
20
|
+
let interactorName = pascalCase(options.name);
|
|
21
|
+
let filters = Object.entries(options.filter.all).map(([name, filter]) => `"${name}": ${matcherCode(filter)}`);
|
|
22
|
+
return `${interactorName}(${[locator, filters.length && `{ ${filters.join(", ")} }`]
|
|
23
|
+
.filter(Boolean)
|
|
24
|
+
.join(", ")})`;
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function serializeInteractionOptions<E extends Element, T>(
|
|
30
|
+
type: InteractionType,
|
|
31
|
+
{ name, interactor: { options }, args, filters }: InteractionOptions<E, T>
|
|
32
|
+
): SerializedInteractionOptions {
|
|
33
|
+
let interactor = serializeInteractorOptions(options);
|
|
34
|
+
let ancestors = options.ancestors.map((ancestor) => serializeInteractorOptions(ancestor));
|
|
35
|
+
return {
|
|
36
|
+
...interactor,
|
|
37
|
+
ancestors,
|
|
38
|
+
name,
|
|
39
|
+
type,
|
|
40
|
+
args,
|
|
41
|
+
code() {
|
|
42
|
+
let serializedArgs = "";
|
|
43
|
+
if (filters) {
|
|
44
|
+
let serializedFilters = Object.entries(filters).map(([name, filter]) => `"${name}": ${matcherCode(filter)}`);
|
|
45
|
+
serializedArgs = `{ ${serializedFilters.join(", ")} }`;
|
|
46
|
+
} else if (args) {
|
|
47
|
+
serializedArgs = args.map((arg) => JSON.stringify(arg)).join(", ");
|
|
48
|
+
}
|
|
49
|
+
return `${[...ancestors, interactor]
|
|
50
|
+
.map(({ code }, index) => (index == 0 ? code() : `${code()})`))
|
|
51
|
+
.join(".find(")}.${name}(${serializedArgs})`;
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
}
|
package/src/specification.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
2
|
|
|
3
|
+
import { Operation } from '@effection/core';
|
|
3
4
|
import { FilterSet } from './filter-set';
|
|
4
5
|
import { Locator } from './locator';
|
|
5
|
-
import {
|
|
6
|
+
import { ActionInteraction, AssertionInteraction } from './interaction';
|
|
6
7
|
import { MergeObjects } from './merge-objects';
|
|
7
8
|
import { MaybeMatcher } from './matcher';
|
|
8
9
|
|
|
@@ -38,7 +39,7 @@ export interface Interactor<E extends Element, F extends FilterParams<any, any>>
|
|
|
38
39
|
* await Link('Next').perform((e) => e.click());
|
|
39
40
|
* ```
|
|
40
41
|
*/
|
|
41
|
-
perform(fn: (element: E) => void):
|
|
42
|
+
perform(fn: (element: E) => void): ActionInteraction<E, void>;
|
|
42
43
|
|
|
43
44
|
/**
|
|
44
45
|
* Perform a one-off assertion on the given interactor. Takes a function which
|
|
@@ -54,7 +55,7 @@ export interface Interactor<E extends Element, F extends FilterParams<any, any>>
|
|
|
54
55
|
* await Link('Next').assert((e) => assert(e.tagName === 'A'));
|
|
55
56
|
* ```
|
|
56
57
|
*/
|
|
57
|
-
assert(fn: (element: E) => void):
|
|
58
|
+
assert(fn: (element: E) => void): AssertionInteraction<E, void>;
|
|
58
59
|
|
|
59
60
|
/**
|
|
60
61
|
* An assertion which checks that an element matching the interactor exists.
|
|
@@ -66,7 +67,7 @@ export interface Interactor<E extends Element, F extends FilterParams<any, any>>
|
|
|
66
67
|
* await Link('Next').exists();
|
|
67
68
|
* ```
|
|
68
69
|
*/
|
|
69
|
-
exists():
|
|
70
|
+
exists(): AssertionInteraction<E, void> & FilterObject<boolean, Element>;
|
|
70
71
|
|
|
71
72
|
/**
|
|
72
73
|
* An assertion which checks that an element matching the interactor does not
|
|
@@ -78,7 +79,7 @@ export interface Interactor<E extends Element, F extends FilterParams<any, any>>
|
|
|
78
79
|
* await Link('Next').absent();
|
|
79
80
|
* ```
|
|
80
81
|
*/
|
|
81
|
-
absent():
|
|
82
|
+
absent(): AssertionInteraction<E, void> & FilterObject<boolean, Element>;
|
|
82
83
|
|
|
83
84
|
/**
|
|
84
85
|
* Checks that there is one element matching the interactor, and that this
|
|
@@ -91,7 +92,7 @@ export interface Interactor<E extends Element, F extends FilterParams<any, any>>
|
|
|
91
92
|
* await Link('Home').has({ href: '/' })
|
|
92
93
|
* ```
|
|
93
94
|
*/
|
|
94
|
-
has(filters: F):
|
|
95
|
+
has(filters: F): AssertionInteraction<E, void>;
|
|
95
96
|
|
|
96
97
|
/**
|
|
97
98
|
* Identical to {@link has}, but reads better with some filters.
|
|
@@ -102,7 +103,7 @@ export interface Interactor<E extends Element, F extends FilterParams<any, any>>
|
|
|
102
103
|
* await CheckBox('Accept conditions').is({ checked: true })
|
|
103
104
|
* ```
|
|
104
105
|
*/
|
|
105
|
-
is(filters: F):
|
|
106
|
+
is(filters: F): AssertionInteraction<E, void>;
|
|
106
107
|
|
|
107
108
|
/**
|
|
108
109
|
* Returns a copy of the given interactor which is scoped to this interactor.
|
|
@@ -128,7 +129,7 @@ export interface Interactor<E extends Element, F extends FilterParams<any, any>>
|
|
|
128
129
|
apply: FilterFn<string, Element>;
|
|
129
130
|
}
|
|
130
131
|
|
|
131
|
-
export type ActionFn<E extends Element, I extends Interactor<E, any>> = (interactor: I, ...args: any[]) =>
|
|
132
|
+
export type ActionFn<E extends Element, I extends Interactor<E, any>> = (interactor: I, ...args: any[]) => Operation<unknown>;
|
|
132
133
|
|
|
133
134
|
export type FilterFn<T, E extends Element> = (element: E) => T;
|
|
134
135
|
|
|
@@ -162,15 +163,15 @@ export type InteractorSpecification<E extends Element, F extends Filters<E>, A e
|
|
|
162
163
|
}
|
|
163
164
|
|
|
164
165
|
export type ActionMethods<E extends Element, A extends Actions<E, I>, I extends Interactor<E, any>> = {
|
|
165
|
-
[P in keyof A]: A[P] extends ((interactor: I, ...args: infer TArgs) =>
|
|
166
|
-
? ((...args: TArgs) =>
|
|
166
|
+
[P in keyof A]: A[P] extends ((interactor: I, ...args: infer TArgs) => Operation<infer TReturn>)
|
|
167
|
+
? ((...args: TArgs) => ActionInteraction<E, TReturn>)
|
|
167
168
|
: never;
|
|
168
169
|
}
|
|
169
170
|
|
|
170
171
|
export type FilterMethods<E extends Element, F extends Filters<E>> = {
|
|
171
172
|
[P in keyof F]:
|
|
172
|
-
F[P] extends FilterFn<infer TReturn, any> ? (() =>
|
|
173
|
-
F[P] extends FilterObject<infer TReturn, any> ? (() =>
|
|
173
|
+
F[P] extends FilterFn<infer TReturn, any> ? (() => AssertionInteraction<E, TReturn> & FilterObject<TReturn, Element>) :
|
|
174
|
+
F[P] extends FilterObject<infer TReturn, any> ? (() => AssertionInteraction<E, TReturn> & FilterObject<TReturn, Element>) :
|
|
174
175
|
never;
|
|
175
176
|
}
|
|
176
177
|
|
|
@@ -198,6 +199,7 @@ export type FilterParams<E extends Element, F extends Filters<E>> = keyof F exte
|
|
|
198
199
|
* @typeParam A the actions of this interactor, this is usually inferred from the specification
|
|
199
200
|
*/
|
|
200
201
|
export interface InteractorConstructor<E extends Element, FP extends FilterParams<any, any>, FM extends FilterMethods<any, any>, AM extends ActionMethods<any, any, any>> {
|
|
202
|
+
interactorName: string;
|
|
201
203
|
selector(value: string | SelectorFn<E>): InteractorConstructor<E, FP, FM, AM>;
|
|
202
204
|
locator(value: FilterDefinition<string, E>): InteractorConstructor<E, FP, FM, AM>;
|
|
203
205
|
filters<FR extends Filters<E>>(filters: FR): InteractorConstructor<E, MergeObjects<FP, FilterParams<E, FR>>, MergeObjects<FM, FilterMethods<E, FR>>, AM>;
|
|
@@ -225,6 +227,7 @@ export interface InteractorConstructor<E extends Element, FP extends FilterParam
|
|
|
225
227
|
*
|
|
226
228
|
* ``` typescript
|
|
227
229
|
* Link('Home');
|
|
230
|
+
* Link(/^home/i);
|
|
228
231
|
* ```
|
|
229
232
|
*
|
|
230
233
|
* Or with a locator and options:
|
|
@@ -236,7 +239,7 @@ export interface InteractorConstructor<E extends Element, FP extends FilterParam
|
|
|
236
239
|
* @param value The locator value, which should match the value of applying the locator function defined in the {@link InteractorSpecification} to the element.
|
|
237
240
|
* @param filters An object describing a set of filters to apply, which should match the value of applying the filters defined in the {@link InteractorSpecification} to the element.
|
|
238
241
|
*/
|
|
239
|
-
(value: MaybeMatcher<string
|
|
242
|
+
(value: MaybeMatcher<string> | RegExp, filters?: FP): Interactor<E, FP> & FM & AM;
|
|
240
243
|
}
|
|
241
244
|
|
|
242
245
|
export type InteractorOptions<E extends Element, F extends Filters<E>, A extends Actions<E, Interactor<E, EmptyObject>>> = {
|
package/dist/cjs/format-table.js
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.formatTable = void 0;
|
|
4
|
-
const MAX_COLUMN_WIDTH = 40;
|
|
5
|
-
function formatValue(value, width) {
|
|
6
|
-
if (value.length > width) {
|
|
7
|
-
return value.slice(0, width - 1) + '…';
|
|
8
|
-
}
|
|
9
|
-
else {
|
|
10
|
-
return value.padEnd(width);
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
function formatTable(options) {
|
|
14
|
-
let columnWidths = options.headers.map((h, index) => {
|
|
15
|
-
return Math.min(MAX_COLUMN_WIDTH, Math.max(h.length, ...options.rows.map((r) => r[index].length)));
|
|
16
|
-
});
|
|
17
|
-
let formatRow = (cells) => {
|
|
18
|
-
return '┃ ' + cells.map((c, index) => formatValue(c, columnWidths[index])).join(' ┃ ') + ' ┃';
|
|
19
|
-
};
|
|
20
|
-
let spacerRow = () => {
|
|
21
|
-
return '┣━' + columnWidths.map((w) => "━".repeat(w)).join('━╋━') + '━┫';
|
|
22
|
-
};
|
|
23
|
-
return [
|
|
24
|
-
formatRow(options.headers),
|
|
25
|
-
spacerRow(),
|
|
26
|
-
...options.rows.map((row) => formatRow(row))
|
|
27
|
-
].join('\n');
|
|
28
|
-
}
|
|
29
|
-
exports.formatTable = formatTable;
|
|
30
|
-
//# sourceMappingURL=format-table.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"format-table.js","sourceRoot":"","sources":["../../src/format-table.ts"],"names":[],"mappings":";;;AAKA,MAAM,gBAAgB,GAAG,EAAE,CAAA;AAE3B,SAAS,WAAW,CAAC,KAAa,EAAE,KAAa;IAC/C,IAAG,KAAK,CAAC,MAAM,GAAG,KAAK,EAAE;QACvB,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC;KACxC;SAAM;QACL,OAAO,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;KAC5B;AACH,CAAC;AAED,SAAgB,WAAW,CAAC,OAAqB;IAC/C,IAAI,YAAY,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE;QAClD,OAAO,IAAI,CAAC,GAAG,CAAC,gBAAgB,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IACrG,CAAC,CAAC,CAAC;IAEH,IAAI,SAAS,GAAG,CAAC,KAAe,EAAE,EAAE;QAClC,OAAO,IAAI,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC,WAAW,CAAC,CAAC,EAAE,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC;IAChG,CAAC,CAAA;IAED,IAAI,SAAS,GAAG,GAAG,EAAE;QACnB,OAAO,IAAI,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC;IAC1E,CAAC,CAAA;IAED,OAAO;QACL,SAAS,CAAC,OAAO,CAAC,OAAO,CAAC;QAC1B,SAAS,EAAE;QACX,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;KAC7C,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC;AAlBD,kCAkBC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"format-table.js","sourceRoot":"","sources":["../../src/format-table.ts"],"names":[],"mappings":"AAKA,MAAM,gBAAgB,GAAG,EAAE,CAAA;AAE3B,SAAS,WAAW,CAAC,KAAa,EAAE,KAAa;IAC/C,IAAG,KAAK,CAAC,MAAM,GAAG,KAAK,EAAE;QACvB,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC;KACxC;SAAM;QACL,OAAO,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;KAC5B;AACH,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,OAAqB;IAC/C,IAAI,YAAY,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE;QAClD,OAAO,IAAI,CAAC,GAAG,CAAC,gBAAgB,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IACrG,CAAC,CAAC,CAAC;IAEH,IAAI,SAAS,GAAG,CAAC,KAAe,EAAE,EAAE;QAClC,OAAO,IAAI,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC,WAAW,CAAC,CAAC,EAAE,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC;IAChG,CAAC,CAAA;IAED,IAAI,SAAS,GAAG,GAAG,EAAE;QACnB,OAAO,IAAI,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC;IAC1E,CAAC,CAAA;IAED,OAAO;QACL,SAAS,CAAC,OAAO,CAAC,OAAO,CAAC;QAC1B,SAAS,EAAE;QACX,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;KAC7C,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC"}
|
package/dist/format-table.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"format-table.d.ts","sourceRoot":"","sources":["../src/format-table.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,IAAI,EAAE,MAAM,EAAE,EAAE,CAAC;CAClB;AAYD,wBAAgB,WAAW,CAAC,OAAO,EAAE,YAAY,GAAG,MAAM,CAkBzD"}
|
package/src/format-table.ts
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
export interface TableOptions {
|
|
2
|
-
headers: string[];
|
|
3
|
-
rows: string[][];
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
const MAX_COLUMN_WIDTH = 40
|
|
7
|
-
|
|
8
|
-
function formatValue(value: string, width: number) {
|
|
9
|
-
if(value.length > width) {
|
|
10
|
-
return value.slice(0, width - 1) + '…';
|
|
11
|
-
} else {
|
|
12
|
-
return value.padEnd(width);
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export function formatTable(options: TableOptions): string {
|
|
17
|
-
let columnWidths = options.headers.map((h, index) => {
|
|
18
|
-
return Math.min(MAX_COLUMN_WIDTH, Math.max(h.length, ...options.rows.map((r) => r[index].length)));
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
let formatRow = (cells: string[]) => {
|
|
22
|
-
return '┃ ' + cells.map((c, index) => formatValue(c, columnWidths[index])).join(' ┃ ') + ' ┃';
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
let spacerRow = () => {
|
|
26
|
-
return '┣━' + columnWidths.map((w) => "━".repeat(w)).join('━╋━') + '━┫';
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
return [
|
|
30
|
-
formatRow(options.headers),
|
|
31
|
-
spacerRow(),
|
|
32
|
-
...options.rows.map((row) => formatRow(row))
|
|
33
|
-
].join('\n');
|
|
34
|
-
}
|