@fabricorg/databricks-bdd 0.3.2 → 0.4.0
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/README.md +41 -3
- package/dist/actions-CuXXKUqe.d.ts +92 -0
- package/dist/actions-CxfxhBZh.d.cts +92 -0
- package/dist/actions.cjs +14 -0
- package/dist/actions.cjs.map +1 -0
- package/dist/actions.d.cts +6 -0
- package/dist/actions.d.ts +6 -0
- package/dist/actions.js +3 -0
- package/dist/actions.js.map +1 -0
- package/dist/cardinality.cjs +24 -0
- package/dist/cardinality.cjs.map +1 -0
- package/dist/cardinality.d.cts +19 -0
- package/dist/cardinality.d.ts +19 -0
- package/dist/cardinality.js +3 -0
- package/dist/cardinality.js.map +1 -0
- package/dist/{chunk-355WDWP3.js → chunk-3XDMBHFP.js} +101 -3
- package/dist/chunk-3XDMBHFP.js.map +1 -0
- package/dist/chunk-6JC65RH2.js +11 -0
- package/dist/chunk-6JC65RH2.js.map +1 -0
- package/dist/chunk-HTKRJLVX.js +70 -0
- package/dist/chunk-HTKRJLVX.js.map +1 -0
- package/dist/chunk-K5LJ7WSW.js +22 -0
- package/dist/chunk-K5LJ7WSW.js.map +1 -0
- package/dist/index.cjs +199 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +24 -65
- package/dist/index.d.ts +24 -65
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/scoped-state.cjs +74 -0
- package/dist/scoped-state.cjs.map +1 -0
- package/dist/scoped-state.d.cts +30 -0
- package/dist/scoped-state.d.ts +30 -0
- package/dist/scoped-state.js +3 -0
- package/dist/scoped-state.js.map +1 -0
- package/dist/steps.cjs +177 -3
- package/dist/steps.cjs.map +1 -1
- package/dist/steps.js +10 -4
- package/dist/steps.js.map +1 -1
- package/package.json +31 -1
- package/dist/chunk-355WDWP3.js.map +0 -1
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
// src/scoped-state.ts
|
|
2
|
+
var SharedStateRegistry = class {
|
|
3
|
+
runValues = /* @__PURE__ */ new Map();
|
|
4
|
+
featureValues = /* @__PURE__ */ new Map();
|
|
5
|
+
run() {
|
|
6
|
+
return this.runValues;
|
|
7
|
+
}
|
|
8
|
+
feature(featureUri) {
|
|
9
|
+
if (!featureUri) throw new Error("Feature state requires a feature URI");
|
|
10
|
+
let values = this.featureValues.get(featureUri);
|
|
11
|
+
if (!values) {
|
|
12
|
+
values = /* @__PURE__ */ new Map();
|
|
13
|
+
this.featureValues.set(featureUri, values);
|
|
14
|
+
}
|
|
15
|
+
return values;
|
|
16
|
+
}
|
|
17
|
+
clear() {
|
|
18
|
+
this.runValues.clear();
|
|
19
|
+
this.featureValues.clear();
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
var ScopedState = class {
|
|
23
|
+
constructor(runValues, featureValues) {
|
|
24
|
+
this.runValues = runValues;
|
|
25
|
+
this.featureValues = featureValues;
|
|
26
|
+
}
|
|
27
|
+
runValues;
|
|
28
|
+
featureValues;
|
|
29
|
+
scenarioValues = /* @__PURE__ */ new Map();
|
|
30
|
+
set(scope, key, value) {
|
|
31
|
+
this.values(scope).set(validateKey(key), value);
|
|
32
|
+
return value;
|
|
33
|
+
}
|
|
34
|
+
get(key) {
|
|
35
|
+
const validKey = validateKey(key);
|
|
36
|
+
if (this.scenarioValues.has(validKey)) return this.scenarioValues.get(validKey);
|
|
37
|
+
if (this.featureValues.has(validKey)) return this.featureValues.get(validKey);
|
|
38
|
+
return this.runValues.get(validKey);
|
|
39
|
+
}
|
|
40
|
+
require(key) {
|
|
41
|
+
const value = this.get(key);
|
|
42
|
+
if (value === void 0) throw new Error(`Required BDD state '${key}' is not set`);
|
|
43
|
+
return value;
|
|
44
|
+
}
|
|
45
|
+
has(key) {
|
|
46
|
+
const validKey = validateKey(key);
|
|
47
|
+
return this.scenarioValues.has(validKey) || this.featureValues.has(validKey) || this.runValues.has(validKey);
|
|
48
|
+
}
|
|
49
|
+
delete(scope, key) {
|
|
50
|
+
return this.values(scope).delete(validateKey(key));
|
|
51
|
+
}
|
|
52
|
+
clearScenario() {
|
|
53
|
+
this.scenarioValues.clear();
|
|
54
|
+
}
|
|
55
|
+
values(scope) {
|
|
56
|
+
if (scope === "run") return this.runValues;
|
|
57
|
+
if (scope === "feature") return this.featureValues;
|
|
58
|
+
return this.scenarioValues;
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
function validateKey(key) {
|
|
62
|
+
const trimmed = key.trim();
|
|
63
|
+
if (!trimmed) throw new Error("BDD state key must not be empty");
|
|
64
|
+
return trimmed;
|
|
65
|
+
}
|
|
66
|
+
var sharedState = new SharedStateRegistry();
|
|
67
|
+
|
|
68
|
+
export { ScopedState, SharedStateRegistry, sharedState };
|
|
69
|
+
//# sourceMappingURL=chunk-HTKRJLVX.js.map
|
|
70
|
+
//# sourceMappingURL=chunk-HTKRJLVX.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/scoped-state.ts"],"names":[],"mappings":"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","file":"chunk-HTKRJLVX.js","sourcesContent":["export type StateScope = 'run' | 'feature' | 'scenario';\n\ntype StateValues = Map<string, unknown>;\n\n/**\n * Process-local state backing Behave-style run and feature context layers.\n * Scenario values stay on the scenario World and are never shared.\n */\nexport class SharedStateRegistry {\n private readonly runValues: StateValues = new Map();\n private readonly featureValues = new Map<string, StateValues>();\n\n run(): StateValues {\n return this.runValues;\n }\n\n feature(featureUri: string): StateValues {\n if (!featureUri) throw new Error('Feature state requires a feature URI');\n let values = this.featureValues.get(featureUri);\n if (!values) {\n values = new Map();\n this.featureValues.set(featureUri, values);\n }\n return values;\n }\n\n clear(): void {\n this.runValues.clear();\n this.featureValues.clear();\n }\n}\n\n/** Layered state lookup: scenario overrides feature, which overrides run. */\nexport class ScopedState {\n private readonly scenarioValues: StateValues = new Map();\n\n constructor(\n private readonly runValues: StateValues,\n private readonly featureValues: StateValues,\n ) {}\n\n set<T>(scope: StateScope, key: string, value: T): T {\n this.values(scope).set(validateKey(key), value);\n return value;\n }\n\n get<T>(key: string): T | undefined {\n const validKey = validateKey(key);\n if (this.scenarioValues.has(validKey)) return this.scenarioValues.get(validKey) as T;\n if (this.featureValues.has(validKey)) return this.featureValues.get(validKey) as T;\n return this.runValues.get(validKey) as T | undefined;\n }\n\n require<T>(key: string): T {\n const value = this.get<T>(key);\n if (value === undefined) throw new Error(`Required BDD state '${key}' is not set`);\n return value;\n }\n\n has(key: string): boolean {\n const validKey = validateKey(key);\n return (\n this.scenarioValues.has(validKey) ||\n this.featureValues.has(validKey) ||\n this.runValues.has(validKey)\n );\n }\n\n delete(scope: StateScope, key: string): boolean {\n return this.values(scope).delete(validateKey(key));\n }\n\n clearScenario(): void {\n this.scenarioValues.clear();\n }\n\n private values(scope: StateScope): StateValues {\n if (scope === 'run') return this.runValues;\n if (scope === 'feature') return this.featureValues;\n return this.scenarioValues;\n }\n}\n\nfunction validateKey(key: string): string {\n const trimmed = key.trim();\n if (!trimmed) throw new Error('BDD state key must not be empty');\n return trimmed;\n}\n\nexport const sharedState = new SharedStateRegistry();\n"]}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// src/cardinality.ts
|
|
2
|
+
function parseCardinalityField(value, cardinality, transform, options = {}) {
|
|
3
|
+
const fieldName = options.fieldName ?? "field";
|
|
4
|
+
const trimmed = value.trim();
|
|
5
|
+
const items = trimmed ? trimmed.split(options.separator ?? /\s*,\s*/).map((item) => item.trim()) : [];
|
|
6
|
+
if (items.some((item) => !item)) {
|
|
7
|
+
throw new Error(`${fieldName} contains an empty value`);
|
|
8
|
+
}
|
|
9
|
+
if (cardinality === "?" && items.length > 1) {
|
|
10
|
+
throw new Error(`${fieldName} expects zero or one value, received ${items.length}`);
|
|
11
|
+
}
|
|
12
|
+
if (cardinality === "+" && items.length === 0) {
|
|
13
|
+
throw new Error(`${fieldName} expects one or more values`);
|
|
14
|
+
}
|
|
15
|
+
const parsed = items.map(transform);
|
|
16
|
+
if (cardinality === "?") return parsed[0];
|
|
17
|
+
return parsed;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export { parseCardinalityField };
|
|
21
|
+
//# sourceMappingURL=chunk-K5LJ7WSW.js.map
|
|
22
|
+
//# sourceMappingURL=chunk-K5LJ7WSW.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cardinality.ts"],"names":[],"mappings":";AAmBO,SAAS,sBACd,KAAA,EACA,WAAA,EACA,SAAA,EACA,OAAA,GAA8B,EAAC,EACN;AACzB,EAAA,MAAM,SAAA,GAAY,QAAQ,SAAA,IAAa,OAAA;AACvC,EAAA,MAAM,OAAA,GAAU,MAAM,IAAA,EAAK;AAC3B,EAAA,MAAM,KAAA,GAAQ,OAAA,GACV,OAAA,CAAQ,KAAA,CAAM,QAAQ,SAAA,IAAa,SAAS,CAAA,CAAE,GAAA,CAAI,CAAC,IAAA,KAAS,IAAA,CAAK,IAAA,EAAM,IACvE,EAAC;AACL,EAAA,IAAI,MAAM,IAAA,CAAK,CAAC,IAAA,KAAS,CAAC,IAAI,CAAA,EAAG;AAC/B,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,EAAG,SAAS,CAAA,wBAAA,CAA0B,CAAA;AAAA,EACxD;AAEA,EAAA,IAAI,WAAA,KAAgB,GAAA,IAAO,KAAA,CAAM,MAAA,GAAS,CAAA,EAAG;AAC3C,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,EAAG,SAAS,CAAA,qCAAA,EAAwC,KAAA,CAAM,MAAM,CAAA,CAAE,CAAA;AAAA,EACpF;AACA,EAAA,IAAI,WAAA,KAAgB,GAAA,IAAO,KAAA,CAAM,MAAA,KAAW,CAAA,EAAG;AAC7C,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,EAAG,SAAS,CAAA,2BAAA,CAA6B,CAAA;AAAA,EAC3D;AAEA,EAAA,MAAM,MAAA,GAAS,KAAA,CAAM,GAAA,CAAI,SAAS,CAAA;AAClC,EAAA,IAAI,WAAA,KAAgB,GAAA,EAAK,OAAO,MAAA,CAAO,CAAC,CAAA;AACxC,EAAA,OAAO,MAAA;AACT","file":"chunk-K5LJ7WSW.js","sourcesContent":["/** Cardinalities supported by Behave's cfparse fields. */\nexport type FieldCardinality = '?' | '*' | '+';\n\nexport interface CardinalityOptions {\n /** Delimiter between values. Defaults to a comma with optional whitespace. */\n separator?: string | RegExp;\n /** Human-readable field name used in validation errors. */\n fieldName?: string;\n}\n\nexport type CardinalityResult<T, C extends FieldCardinality> = C extends '?' ? T | undefined : T[];\n\n/**\n * Parse a Cucumber string parameter with cfparse-compatible cardinality.\n *\n * Use `?` for zero-or-one, `*` for zero-or-more, and `+` for one-or-more.\n * This keeps the capability in TypeScript while allowing standard Cucumber\n * Expressions and readable comma-separated parameters.\n */\nexport function parseCardinalityField<T, C extends FieldCardinality>(\n value: string,\n cardinality: C,\n transform: (item: string, index: number) => T,\n options: CardinalityOptions = {},\n): CardinalityResult<T, C> {\n const fieldName = options.fieldName ?? 'field';\n const trimmed = value.trim();\n const items = trimmed\n ? trimmed.split(options.separator ?? /\\s*,\\s*/).map((item) => item.trim())\n : [];\n if (items.some((item) => !item)) {\n throw new Error(`${fieldName} contains an empty value`);\n }\n\n if (cardinality === '?' && items.length > 1) {\n throw new Error(`${fieldName} expects zero or one value, received ${items.length}`);\n }\n if (cardinality === '+' && items.length === 0) {\n throw new Error(`${fieldName} expects one or more values`);\n }\n\n const parsed = items.map(transform);\n if (cardinality === '?') return parsed[0] as CardinalityResult<T, C>;\n return parsed as CardinalityResult<T, C>;\n}\n"]}
|
package/dist/index.cjs
CHANGED
|
@@ -11,6 +11,14 @@ var util = require('util');
|
|
|
11
11
|
var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
|
|
12
12
|
// src/world.ts
|
|
13
13
|
|
|
14
|
+
// src/actions.ts
|
|
15
|
+
function defineAction(action) {
|
|
16
|
+
return action;
|
|
17
|
+
}
|
|
18
|
+
async function runAction(world, action, ...args) {
|
|
19
|
+
return await action(world, ...args);
|
|
20
|
+
}
|
|
21
|
+
|
|
14
22
|
// src/fixtures.ts
|
|
15
23
|
function isFixtureResult(value) {
|
|
16
24
|
return typeof value === "object" && value !== null && Object.prototype.hasOwnProperty.call(value, "value") && typeof value.cleanup === "function";
|
|
@@ -70,6 +78,73 @@ var SharedFixtureRegistry = class {
|
|
|
70
78
|
};
|
|
71
79
|
var sharedFixtures = new SharedFixtureRegistry();
|
|
72
80
|
|
|
81
|
+
// src/scoped-state.ts
|
|
82
|
+
var SharedStateRegistry = class {
|
|
83
|
+
runValues = /* @__PURE__ */ new Map();
|
|
84
|
+
featureValues = /* @__PURE__ */ new Map();
|
|
85
|
+
run() {
|
|
86
|
+
return this.runValues;
|
|
87
|
+
}
|
|
88
|
+
feature(featureUri) {
|
|
89
|
+
if (!featureUri) throw new Error("Feature state requires a feature URI");
|
|
90
|
+
let values = this.featureValues.get(featureUri);
|
|
91
|
+
if (!values) {
|
|
92
|
+
values = /* @__PURE__ */ new Map();
|
|
93
|
+
this.featureValues.set(featureUri, values);
|
|
94
|
+
}
|
|
95
|
+
return values;
|
|
96
|
+
}
|
|
97
|
+
clear() {
|
|
98
|
+
this.runValues.clear();
|
|
99
|
+
this.featureValues.clear();
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
var ScopedState = class {
|
|
103
|
+
constructor(runValues, featureValues) {
|
|
104
|
+
this.runValues = runValues;
|
|
105
|
+
this.featureValues = featureValues;
|
|
106
|
+
}
|
|
107
|
+
runValues;
|
|
108
|
+
featureValues;
|
|
109
|
+
scenarioValues = /* @__PURE__ */ new Map();
|
|
110
|
+
set(scope, key, value) {
|
|
111
|
+
this.values(scope).set(validateKey(key), value);
|
|
112
|
+
return value;
|
|
113
|
+
}
|
|
114
|
+
get(key) {
|
|
115
|
+
const validKey = validateKey(key);
|
|
116
|
+
if (this.scenarioValues.has(validKey)) return this.scenarioValues.get(validKey);
|
|
117
|
+
if (this.featureValues.has(validKey)) return this.featureValues.get(validKey);
|
|
118
|
+
return this.runValues.get(validKey);
|
|
119
|
+
}
|
|
120
|
+
require(key) {
|
|
121
|
+
const value = this.get(key);
|
|
122
|
+
if (value === void 0) throw new Error(`Required BDD state '${key}' is not set`);
|
|
123
|
+
return value;
|
|
124
|
+
}
|
|
125
|
+
has(key) {
|
|
126
|
+
const validKey = validateKey(key);
|
|
127
|
+
return this.scenarioValues.has(validKey) || this.featureValues.has(validKey) || this.runValues.has(validKey);
|
|
128
|
+
}
|
|
129
|
+
delete(scope, key) {
|
|
130
|
+
return this.values(scope).delete(validateKey(key));
|
|
131
|
+
}
|
|
132
|
+
clearScenario() {
|
|
133
|
+
this.scenarioValues.clear();
|
|
134
|
+
}
|
|
135
|
+
values(scope) {
|
|
136
|
+
if (scope === "run") return this.runValues;
|
|
137
|
+
if (scope === "feature") return this.featureValues;
|
|
138
|
+
return this.scenarioValues;
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
function validateKey(key) {
|
|
142
|
+
const trimmed = key.trim();
|
|
143
|
+
if (!trimmed) throw new Error("BDD state key must not be empty");
|
|
144
|
+
return trimmed;
|
|
145
|
+
}
|
|
146
|
+
var sharedState = new SharedStateRegistry();
|
|
147
|
+
|
|
73
148
|
// src/world.ts
|
|
74
149
|
var DatabricksWorld = class extends cucumber.World {
|
|
75
150
|
profile;
|
|
@@ -86,10 +161,13 @@ var DatabricksWorld = class extends cucumber.World {
|
|
|
86
161
|
lastSql;
|
|
87
162
|
lastStatementId;
|
|
88
163
|
featureUri;
|
|
164
|
+
scenarioName;
|
|
165
|
+
effectiveTags = [];
|
|
89
166
|
stepOutputCapture;
|
|
90
167
|
lakebasePool;
|
|
91
168
|
ctx;
|
|
92
169
|
activeCtx;
|
|
170
|
+
scopedState;
|
|
93
171
|
cleanups = [];
|
|
94
172
|
constructor(options) {
|
|
95
173
|
super(options);
|
|
@@ -124,6 +202,29 @@ var DatabricksWorld = class extends cucumber.World {
|
|
|
124
202
|
addCleanup(cleanup) {
|
|
125
203
|
this.cleanups.push(cleanup);
|
|
126
204
|
}
|
|
205
|
+
/** Initialize Behave-style feature/scenario metadata and layered state. */
|
|
206
|
+
initializeScenario(featureUri, scenarioName, tags) {
|
|
207
|
+
this.featureUri = featureUri;
|
|
208
|
+
this.scenarioName = scenarioName;
|
|
209
|
+
this.effectiveTags = [...tags];
|
|
210
|
+
this.scopedState = new ScopedState(sharedState.run(), sharedState.feature(featureUri));
|
|
211
|
+
}
|
|
212
|
+
/** Set typed state at run, feature, or scenario scope. */
|
|
213
|
+
setState(scope, key, value) {
|
|
214
|
+
return this.state().set(scope, key, value);
|
|
215
|
+
}
|
|
216
|
+
/** Resolve state using scenario → feature → run precedence. */
|
|
217
|
+
getState(key) {
|
|
218
|
+
return this.state().get(key);
|
|
219
|
+
}
|
|
220
|
+
/** Resolve required layered state or fail with a useful message. */
|
|
221
|
+
requireState(key) {
|
|
222
|
+
return this.state().require(key);
|
|
223
|
+
}
|
|
224
|
+
/** Compose a reusable typed action without reparsing textual Gherkin. */
|
|
225
|
+
runAction(action, ...args) {
|
|
226
|
+
return runAction(this, action, ...args);
|
|
227
|
+
}
|
|
127
228
|
/** Setup a resource once for this Cucumber run and clean it up in AfterAll. */
|
|
128
229
|
runFixture(key, setup) {
|
|
129
230
|
return sharedFixtures.runFixture(key, setup);
|
|
@@ -170,8 +271,16 @@ var DatabricksWorld = class extends cucumber.World {
|
|
|
170
271
|
errors.push(error);
|
|
171
272
|
}
|
|
172
273
|
this.ctx = void 0;
|
|
274
|
+
this.scopedState?.clearScenario();
|
|
275
|
+
this.scopedState = void 0;
|
|
173
276
|
if (errors.length > 0) throw new AggregateError(errors, "Scenario cleanup failed");
|
|
174
277
|
}
|
|
278
|
+
state() {
|
|
279
|
+
if (!this.scopedState) {
|
|
280
|
+
throw new Error("BDD scoped state is only available after the scenario Before hook");
|
|
281
|
+
}
|
|
282
|
+
return this.scopedState;
|
|
283
|
+
}
|
|
175
284
|
};
|
|
176
285
|
|
|
177
286
|
// src/profile.ts
|
|
@@ -190,6 +299,68 @@ function liveOnlyReason(tags, profile) {
|
|
|
190
299
|
if (blocking.length === 0) return null;
|
|
191
300
|
return `requires a live workspace (${blocking.join(", ")}); running under DBX_TEST_PROFILE=local`;
|
|
192
301
|
}
|
|
302
|
+
function capabilitiesFromEnv(profile, env = process.env) {
|
|
303
|
+
const capabilities = { profile };
|
|
304
|
+
const builtins = [
|
|
305
|
+
["cloud", env.DBX_TEST_CLOUD],
|
|
306
|
+
["compute", env.DBX_TEST_COMPUTE_MODE ?? env.DBX_TEST_COMPUTE],
|
|
307
|
+
["stage", env.DBX_TEST_STAGE]
|
|
308
|
+
];
|
|
309
|
+
for (const [key, value] of builtins) {
|
|
310
|
+
if (value !== void 0 && value !== "") capabilities[key] = value;
|
|
311
|
+
}
|
|
312
|
+
for (const [name, value] of Object.entries(env)) {
|
|
313
|
+
if (!name.startsWith("DBX_TEST_CAPABILITY_") || value === void 0) continue;
|
|
314
|
+
capabilities[normalizeCapabilityKey(name.slice("DBX_TEST_CAPABILITY_".length))] = value;
|
|
315
|
+
}
|
|
316
|
+
return capabilities;
|
|
317
|
+
}
|
|
318
|
+
function capabilityTagReason(tags, capabilities) {
|
|
319
|
+
const normalized = new Map(
|
|
320
|
+
Object.entries(capabilities).map(([key, value]) => [
|
|
321
|
+
normalizeCapabilityKey(key),
|
|
322
|
+
String(value).toLowerCase()
|
|
323
|
+
])
|
|
324
|
+
);
|
|
325
|
+
for (const tag of tags) {
|
|
326
|
+
const predicate = parseCapabilityTag(tag);
|
|
327
|
+
if (!predicate) continue;
|
|
328
|
+
const actual = normalized.get(normalizeCapabilityKey(predicate.key));
|
|
329
|
+
const matches = predicate.expected ? actual === predicate.expected.toLowerCase() : actual !== void 0 && !["", "0", "false", "no", "off"].includes(actual);
|
|
330
|
+
if (predicate.kind === "requires" && !matches) {
|
|
331
|
+
return `${tag} requires capability ${predicate.key}${predicate.expected ? `=${predicate.expected}` : ""}; actual ${actual ?? "unset"}`;
|
|
332
|
+
}
|
|
333
|
+
if (predicate.kind === "excludes" && matches) {
|
|
334
|
+
return `${tag} excludes capability ${predicate.key}${predicate.expected ? `=${predicate.expected}` : ""}`;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
return null;
|
|
338
|
+
}
|
|
339
|
+
function scenarioSkipReason(tags, profile, env = process.env) {
|
|
340
|
+
return liveOnlyReason(tags, profile) ?? capabilityTagReason(tags, capabilitiesFromEnv(profile, env));
|
|
341
|
+
}
|
|
342
|
+
function parseCapabilityTag(tag) {
|
|
343
|
+
const direct = tag.match(/^@(requires|excludes)\.([^=]+)(?:=(.+))?$/i);
|
|
344
|
+
if (direct?.[1] && direct[2]) {
|
|
345
|
+
return {
|
|
346
|
+
kind: direct[1].toLowerCase(),
|
|
347
|
+
key: direct[2],
|
|
348
|
+
...direct[3] ? { expected: direct[3] } : {}
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
const behave = tag.match(/^@(use|not)\.with_([^=]+)=(.+)$/i);
|
|
352
|
+
if (behave?.[1] && behave[2] && behave[3]) {
|
|
353
|
+
return {
|
|
354
|
+
kind: behave[1].toLowerCase() === "use" ? "requires" : "excludes",
|
|
355
|
+
key: behave[2],
|
|
356
|
+
expected: behave[3]
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
return void 0;
|
|
360
|
+
}
|
|
361
|
+
function normalizeCapabilityKey(key) {
|
|
362
|
+
return key.trim().toLowerCase().replace(/[^a-z0-9]+/g, "_");
|
|
363
|
+
}
|
|
193
364
|
|
|
194
365
|
// src/infer.ts
|
|
195
366
|
var INT_RE = /^-?\d+$/;
|
|
@@ -454,6 +625,25 @@ function renderChunks(chunks) {
|
|
|
454
625
|
return rendered.trimEnd();
|
|
455
626
|
}
|
|
456
627
|
|
|
628
|
+
// src/cardinality.ts
|
|
629
|
+
function parseCardinalityField(value, cardinality, transform, options = {}) {
|
|
630
|
+
const fieldName = options.fieldName ?? "field";
|
|
631
|
+
const trimmed = value.trim();
|
|
632
|
+
const items = trimmed ? trimmed.split(options.separator ?? /\s*,\s*/).map((item) => item.trim()) : [];
|
|
633
|
+
if (items.some((item) => !item)) {
|
|
634
|
+
throw new Error(`${fieldName} contains an empty value`);
|
|
635
|
+
}
|
|
636
|
+
if (cardinality === "?" && items.length > 1) {
|
|
637
|
+
throw new Error(`${fieldName} expects zero or one value, received ${items.length}`);
|
|
638
|
+
}
|
|
639
|
+
if (cardinality === "+" && items.length === 0) {
|
|
640
|
+
throw new Error(`${fieldName} expects one or more values`);
|
|
641
|
+
}
|
|
642
|
+
const parsed = items.map(transform);
|
|
643
|
+
if (cardinality === "?") return parsed[0];
|
|
644
|
+
return parsed;
|
|
645
|
+
}
|
|
646
|
+
|
|
457
647
|
Object.defineProperty(exports, "waitForPipelineUpdate", {
|
|
458
648
|
enumerable: true,
|
|
459
649
|
get: function () { return databricksTestkit.waitForPipelineUpdate; }
|
|
@@ -462,19 +652,28 @@ exports.DatabricksWorld = DatabricksWorld;
|
|
|
462
652
|
exports.FAILURE_EVIDENCE_MEDIA_TYPE = FAILURE_EVIDENCE_MEDIA_TYPE;
|
|
463
653
|
exports.LIVE_ONLY_TAGS = LIVE_ONLY_TAGS;
|
|
464
654
|
exports.RUN_STATES = RUN_STATES;
|
|
655
|
+
exports.ScopedState = ScopedState;
|
|
465
656
|
exports.SharedFixtureRegistry = SharedFixtureRegistry;
|
|
657
|
+
exports.SharedStateRegistry = SharedStateRegistry;
|
|
658
|
+
exports.capabilitiesFromEnv = capabilitiesFromEnv;
|
|
659
|
+
exports.capabilityTagReason = capabilityTagReason;
|
|
466
660
|
exports.coerceMatchRows = coerceMatchRows;
|
|
661
|
+
exports.defineAction = defineAction;
|
|
467
662
|
exports.inferFixture = inferFixture;
|
|
468
663
|
exports.liveOnlyReason = liveOnlyReason;
|
|
664
|
+
exports.parseCardinalityField = parseCardinalityField;
|
|
469
665
|
exports.parseDuration = parseDuration;
|
|
470
666
|
exports.parseFailureEvidenceAttachment = parseFailureEvidenceAttachment;
|
|
471
667
|
exports.parseTableIdentifier = parseTableIdentifier;
|
|
472
668
|
exports.redactSecrets = redactSecrets;
|
|
473
669
|
exports.resolveJobId = resolveJobId;
|
|
474
670
|
exports.resolvePipelineId = resolvePipelineId;
|
|
671
|
+
exports.runAction = runAction;
|
|
475
672
|
exports.runFeatures = runFeatures;
|
|
476
673
|
exports.runJobToTerminal = runJobToTerminal;
|
|
674
|
+
exports.scenarioSkipReason = scenarioSkipReason;
|
|
477
675
|
exports.sharedFixtures = sharedFixtures;
|
|
676
|
+
exports.sharedState = sharedState;
|
|
478
677
|
exports.startPipelineUpdate = startPipelineUpdate;
|
|
479
678
|
exports.startStepOutputCapture = startStepOutputCapture;
|
|
480
679
|
//# sourceMappingURL=index.cjs.map
|