@stripe/extensibility-test-helpers 0.2.7
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/LICENSE.md +19 -0
- package/dist/extensibility-test-helpers-alpha.d.ts +218 -0
- package/dist/extensibility-test-helpers-beta.d.ts +218 -0
- package/dist/extensibility-test-helpers-internal.d.ts +218 -0
- package/dist/extensibility-test-helpers-public.d.ts +218 -0
- package/dist/index.cjs +248 -0
- package/dist/index.d.ts +207 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +222 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -0
- package/package.json +44 -0
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@stripe/extensibility-test-helpers`
|
|
3
|
+
*
|
|
4
|
+
* Generic test client for Stripe resources.
|
|
5
|
+
*
|
|
6
|
+
* Works with both CustomObject subclasses and API objects like Customer.
|
|
7
|
+
* Backend implementations can be swapped for local testing vs sandbox.
|
|
8
|
+
*
|
|
9
|
+
* @packageDocumentation
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Backend interface for storage/API operations.
|
|
14
|
+
* Implement for different environments (local, sandbox, production).
|
|
15
|
+
* @public
|
|
16
|
+
*/
|
|
17
|
+
export declare interface Backend {
|
|
18
|
+
/**
|
|
19
|
+
* Creates a new resource of the given object type.
|
|
20
|
+
* @param objectType - The Stripe object type string (e.g. `"customer"`).
|
|
21
|
+
* @param data - Fields to set on the new resource.
|
|
22
|
+
* @returns The created resource with a generated `id`.
|
|
23
|
+
*/
|
|
24
|
+
create<T extends StripeResource>(objectType: string, data: Record<string, unknown>): Promise<T>;
|
|
25
|
+
/**
|
|
26
|
+
* Retrieves a resource by ID.
|
|
27
|
+
* @param objectType - The Stripe object type string.
|
|
28
|
+
* @param id - The resource ID.
|
|
29
|
+
* @returns The matching resource.
|
|
30
|
+
*/
|
|
31
|
+
retrieve<T extends StripeResource>(objectType: string, id: string): Promise<T>;
|
|
32
|
+
/**
|
|
33
|
+
* Updates an existing resource by merging the provided fields.
|
|
34
|
+
* @param objectType - The Stripe object type string.
|
|
35
|
+
* @param id - The resource ID.
|
|
36
|
+
* @param data - Fields to merge into the existing resource.
|
|
37
|
+
* @returns The updated resource.
|
|
38
|
+
*/
|
|
39
|
+
update<T extends StripeResource>(objectType: string, id: string, data: Record<string, unknown>): Promise<T>;
|
|
40
|
+
/**
|
|
41
|
+
* Deletes a resource by ID.
|
|
42
|
+
* @param objectType - The Stripe object type string.
|
|
43
|
+
* @param id - The resource ID.
|
|
44
|
+
*/
|
|
45
|
+
delete(objectType: string, id: string): Promise<void>;
|
|
46
|
+
/**
|
|
47
|
+
* Lists resources of the given object type with optional pagination.
|
|
48
|
+
* @param objectType - The Stripe object type string.
|
|
49
|
+
* @param params - Optional pagination parameters.
|
|
50
|
+
* @returns A paginated list response.
|
|
51
|
+
*/
|
|
52
|
+
list<T extends StripeResource>(objectType: string, params?: ListParams): Promise<ListResponse<T>>;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Input type for create - user-provided fields only.
|
|
57
|
+
*
|
|
58
|
+
* @public
|
|
59
|
+
*/
|
|
60
|
+
export declare type CreateInput<T extends StripeResource> = Partial<Omit<T, 'id' | 'object'>>;
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* List pagination params.
|
|
64
|
+
* @public
|
|
65
|
+
*/
|
|
66
|
+
export declare interface ListParams {
|
|
67
|
+
/** Maximum number of items to return. Defaults to `10`. */
|
|
68
|
+
limit?: number;
|
|
69
|
+
/** Return items after the item with this ID (exclusive). Use for forward pagination. */
|
|
70
|
+
startingAfter?: string;
|
|
71
|
+
/** Return items before the item with this ID (exclusive). Use for backward pagination. */
|
|
72
|
+
endingBefore?: string;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Paginated list response.
|
|
77
|
+
* @public
|
|
78
|
+
*/
|
|
79
|
+
export declare interface ListResponse<T> {
|
|
80
|
+
/** The items in the current page. */
|
|
81
|
+
data: T[];
|
|
82
|
+
/** Whether additional items exist beyond this page. */
|
|
83
|
+
hasMore: boolean;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* In-memory {@link Backend} implementation for use in unit tests.
|
|
88
|
+
*
|
|
89
|
+
* Each instance maintains its own isolated store. Use a shared instance
|
|
90
|
+
* when you need multiple clients to see the same data.
|
|
91
|
+
* @public
|
|
92
|
+
*/
|
|
93
|
+
export declare class LocalBackend implements Backend {
|
|
94
|
+
private store;
|
|
95
|
+
private idCounter;
|
|
96
|
+
private getCollection;
|
|
97
|
+
private generateId;
|
|
98
|
+
/** {@inheritDoc Backend.create} */
|
|
99
|
+
create<T extends StripeResource>(objectType: string, data: Record<string, unknown>): Promise<T>;
|
|
100
|
+
/** {@inheritDoc Backend.retrieve} */
|
|
101
|
+
retrieve<T extends StripeResource>(objectType: string, id: string): Promise<T>;
|
|
102
|
+
/** {@inheritDoc Backend.update} */
|
|
103
|
+
update<T extends StripeResource>(objectType: string, id: string, data: Record<string, unknown>): Promise<T>;
|
|
104
|
+
/** {@inheritDoc Backend.delete} */
|
|
105
|
+
delete(objectType: string, id: string): Promise<void>;
|
|
106
|
+
/** {@inheritDoc Backend.list} */
|
|
107
|
+
list<T extends StripeResource>(objectType: string, params?: ListParams): Promise<ListResponse<T>>;
|
|
108
|
+
/** Resets all stored objects and the ID counter. Useful in `beforeEach` hooks. */
|
|
109
|
+
clear(): void;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* A class constructor that produces instances of T.
|
|
114
|
+
* @public
|
|
115
|
+
*/
|
|
116
|
+
export declare type ResourceClass<T extends StripeResource> = new (...args: unknown[]) => T;
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Minimal interface for any Stripe resource.
|
|
120
|
+
* Both CustomObject and API objects satisfy this.
|
|
121
|
+
* @public
|
|
122
|
+
*/
|
|
123
|
+
export declare interface StripeResource {
|
|
124
|
+
/** Unique identifier for the resource. */
|
|
125
|
+
id: string;
|
|
126
|
+
/** String identifying the object type, e.g. `"customer"` or `"custom_object.order"`. */
|
|
127
|
+
object: string;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Generic test client for any Stripe resource.
|
|
132
|
+
*
|
|
133
|
+
* @example
|
|
134
|
+
* ```ts
|
|
135
|
+
* const locations = new TestClient(Location);
|
|
136
|
+
* const customers = new TestClient(Customer);
|
|
137
|
+
*
|
|
138
|
+
* const loc = await locations.create({ address: '123 Main' });
|
|
139
|
+
* const cust = await customers.create({ email: 'test@example.com' });
|
|
140
|
+
* ```
|
|
141
|
+
* @public
|
|
142
|
+
*/
|
|
143
|
+
export declare class TestClient<T extends StripeResource> {
|
|
144
|
+
private ResourceClass;
|
|
145
|
+
private static sharedBackend;
|
|
146
|
+
private objectType;
|
|
147
|
+
private backend;
|
|
148
|
+
constructor(ResourceClass: ResourceClass<T>, options?: TestClientOptions);
|
|
149
|
+
/**
|
|
150
|
+
* Derive object type by instantiating the class and reading its `object` field.
|
|
151
|
+
*
|
|
152
|
+
* This works for classes that set `object` in their constructor or as a field
|
|
153
|
+
* initializer (e.g. `object = 'test.resource'`). It does **not** work for
|
|
154
|
+
* custom object classes that extend `BaseObject`, because `BaseObject.object`
|
|
155
|
+
* is only set after the build-time code transform runs — plain construction
|
|
156
|
+
* leaves it undefined. For those classes, pass `objectType` explicitly in the
|
|
157
|
+
* constructor options.
|
|
158
|
+
*/
|
|
159
|
+
private deriveObjectType;
|
|
160
|
+
/**
|
|
161
|
+
* Hydrate plain data into a class instance.
|
|
162
|
+
* Returns an instance with the class's prototype (so methods work).
|
|
163
|
+
*/
|
|
164
|
+
private hydrate;
|
|
165
|
+
/**
|
|
166
|
+
* Creates a new resource and returns it as a hydrated class instance.
|
|
167
|
+
*
|
|
168
|
+
* Injects standard platform field defaults (`created`, `updated`, `livemode`)
|
|
169
|
+
* before the caller's data, so the returned instance has the same shape a real
|
|
170
|
+
* Stripe backend would produce. Callers can override any default via `data`.
|
|
171
|
+
*
|
|
172
|
+
* @param data - Fields to set on the new resource (excluding `id` and `object`).
|
|
173
|
+
*/
|
|
174
|
+
create(data: CreateInput<T>): Promise<T>;
|
|
175
|
+
/**
|
|
176
|
+
* Retrieves a resource by ID and returns it as a hydrated class instance.
|
|
177
|
+
* @param id - The resource ID.
|
|
178
|
+
*/
|
|
179
|
+
retrieve(id: string): Promise<T>;
|
|
180
|
+
/**
|
|
181
|
+
* Merges the provided fields into an existing resource and returns the updated instance.
|
|
182
|
+
* @param id - The resource ID.
|
|
183
|
+
* @param data - Fields to merge into the existing resource (excluding `id` and `object`).
|
|
184
|
+
*/
|
|
185
|
+
update(id: string, data: UpdateInput<T>): Promise<T>;
|
|
186
|
+
/**
|
|
187
|
+
* Deletes a resource by ID.
|
|
188
|
+
* @param id - The resource ID.
|
|
189
|
+
*/
|
|
190
|
+
delete(id: string): Promise<void>;
|
|
191
|
+
/**
|
|
192
|
+
* Lists resources with optional pagination, returning hydrated class instances.
|
|
193
|
+
* @param params - Optional pagination parameters.
|
|
194
|
+
*/
|
|
195
|
+
list(params?: ListParams): Promise<ListResponse<T>>;
|
|
196
|
+
/** Clear all test data across all object types */
|
|
197
|
+
static clearAll(): void;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Options for constructing a {@link TestClient}.
|
|
202
|
+
* @public
|
|
203
|
+
*/
|
|
204
|
+
export declare interface TestClientOptions {
|
|
205
|
+
/** Explicit object type (if auto-derive doesn't work) */
|
|
206
|
+
objectType?: string;
|
|
207
|
+
/** Custom backend (defaults to shared LocalBackend) */
|
|
208
|
+
backend?: Backend;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Input type for update - partial user fields.
|
|
213
|
+
*
|
|
214
|
+
* @public
|
|
215
|
+
*/
|
|
216
|
+
export declare type UpdateInput<T extends StripeResource> = Partial<Omit<T, 'id' | 'object'>>;
|
|
217
|
+
|
|
218
|
+
export { }
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
LocalBackend: () => LocalBackend,
|
|
24
|
+
TestClient: () => TestClient
|
|
25
|
+
});
|
|
26
|
+
module.exports = __toCommonJS(index_exports);
|
|
27
|
+
var LocalBackend = class {
|
|
28
|
+
store = /* @__PURE__ */ new Map();
|
|
29
|
+
idCounter = 0;
|
|
30
|
+
getCollection(objectType) {
|
|
31
|
+
if (!this.store.has(objectType)) {
|
|
32
|
+
this.store.set(objectType, /* @__PURE__ */ new Map());
|
|
33
|
+
}
|
|
34
|
+
return this.store.get(objectType);
|
|
35
|
+
}
|
|
36
|
+
generateId() {
|
|
37
|
+
return `obj_test_${String(++this.idCounter)}`;
|
|
38
|
+
}
|
|
39
|
+
/** {@inheritDoc Backend.create} */
|
|
40
|
+
create(objectType, data) {
|
|
41
|
+
const id = this.generateId();
|
|
42
|
+
const object = {
|
|
43
|
+
id,
|
|
44
|
+
object: objectType,
|
|
45
|
+
...data
|
|
46
|
+
};
|
|
47
|
+
this.getCollection(objectType).set(id, object);
|
|
48
|
+
return Promise.resolve(object);
|
|
49
|
+
}
|
|
50
|
+
/** {@inheritDoc Backend.retrieve} */
|
|
51
|
+
retrieve(objectType, id) {
|
|
52
|
+
const object = this.getCollection(objectType).get(id);
|
|
53
|
+
if (!object) {
|
|
54
|
+
return Promise.reject(new Error(`Object not found: ${objectType}/${id}`));
|
|
55
|
+
}
|
|
56
|
+
return Promise.resolve(object);
|
|
57
|
+
}
|
|
58
|
+
/** {@inheritDoc Backend.update} */
|
|
59
|
+
update(objectType, id, data) {
|
|
60
|
+
const object = this.getCollection(objectType).get(id);
|
|
61
|
+
if (!object) {
|
|
62
|
+
return Promise.reject(new Error(`Object not found: ${objectType}/${id}`));
|
|
63
|
+
}
|
|
64
|
+
const updated = { ...object, ...data };
|
|
65
|
+
this.getCollection(objectType).set(id, updated);
|
|
66
|
+
return Promise.resolve(updated);
|
|
67
|
+
}
|
|
68
|
+
/** {@inheritDoc Backend.delete} */
|
|
69
|
+
delete(objectType, id) {
|
|
70
|
+
const collection = this.getCollection(objectType);
|
|
71
|
+
if (!collection.has(id)) {
|
|
72
|
+
return Promise.reject(new Error(`Object not found: ${objectType}/${id}`));
|
|
73
|
+
}
|
|
74
|
+
collection.delete(id);
|
|
75
|
+
return Promise.resolve();
|
|
76
|
+
}
|
|
77
|
+
/** {@inheritDoc Backend.list} */
|
|
78
|
+
list(objectType, params) {
|
|
79
|
+
const collection = this.getCollection(objectType);
|
|
80
|
+
const all = Array.from(collection.values());
|
|
81
|
+
const limit = params?.limit ?? 10;
|
|
82
|
+
const hasStartingAfter = params?.startingAfter !== void 0;
|
|
83
|
+
const hasEndingBefore = params?.endingBefore !== void 0;
|
|
84
|
+
let startIndex = 0;
|
|
85
|
+
let endIndex = all.length;
|
|
86
|
+
if (hasStartingAfter) {
|
|
87
|
+
const idx = all.findIndex((o) => o.id === params.startingAfter);
|
|
88
|
+
if (idx >= 0) {
|
|
89
|
+
startIndex = idx + 1;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
if (hasEndingBefore) {
|
|
93
|
+
const idx = all.findIndex((o) => o.id === params.endingBefore);
|
|
94
|
+
if (idx >= 0) {
|
|
95
|
+
endIndex = idx;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
const windowed = all.slice(startIndex, endIndex);
|
|
99
|
+
const data = hasEndingBefore && !hasStartingAfter && windowed.length > limit ? windowed.slice(windowed.length - limit) : windowed.slice(0, limit);
|
|
100
|
+
const hasMore = limit < windowed.length;
|
|
101
|
+
return Promise.resolve({ data, hasMore });
|
|
102
|
+
}
|
|
103
|
+
/** Resets all stored objects and the ID counter. Useful in `beforeEach` hooks. */
|
|
104
|
+
clear() {
|
|
105
|
+
this.store.clear();
|
|
106
|
+
this.idCounter = 0;
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
var TestClient = class _TestClient {
|
|
110
|
+
constructor(ResourceClass, options) {
|
|
111
|
+
this.ResourceClass = ResourceClass;
|
|
112
|
+
this.backend = options?.backend ?? _TestClient.sharedBackend;
|
|
113
|
+
this.objectType = options?.objectType ?? this.deriveObjectType();
|
|
114
|
+
}
|
|
115
|
+
static sharedBackend = new LocalBackend();
|
|
116
|
+
objectType;
|
|
117
|
+
backend;
|
|
118
|
+
/**
|
|
119
|
+
* Derive object type by instantiating the class and reading its `object` field.
|
|
120
|
+
*
|
|
121
|
+
* This works for classes that set `object` in their constructor or as a field
|
|
122
|
+
* initializer (e.g. `object = 'test.resource'`). It does **not** work for
|
|
123
|
+
* custom object classes that extend `BaseObject`, because `BaseObject.object`
|
|
124
|
+
* is only set after the build-time code transform runs — plain construction
|
|
125
|
+
* leaves it undefined. For those classes, pass `objectType` explicitly in the
|
|
126
|
+
* constructor options.
|
|
127
|
+
*/
|
|
128
|
+
deriveObjectType() {
|
|
129
|
+
let instance;
|
|
130
|
+
try {
|
|
131
|
+
instance = new this.ResourceClass();
|
|
132
|
+
} catch (cause) {
|
|
133
|
+
throw new Error(
|
|
134
|
+
`Could not derive object type for ${this.ResourceClass.name}. Pass it explicitly: new TestClient(${this.ResourceClass.name}, { objectType: '...' })`,
|
|
135
|
+
{ cause: cause instanceof Error ? cause : void 0 }
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
if (!instance.object) {
|
|
139
|
+
throw new Error(
|
|
140
|
+
`${this.ResourceClass.name}.object is undefined after construction. Pass it explicitly: new TestClient(${this.ResourceClass.name}, { objectType: '...' })`
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
return instance.object;
|
|
144
|
+
}
|
|
145
|
+
// -------------------------------------------------------------------------
|
|
146
|
+
// Hydration - convert plain data to class instances
|
|
147
|
+
// -------------------------------------------------------------------------
|
|
148
|
+
// TODO: Handle custom constructors. Options to consider:
|
|
149
|
+
//
|
|
150
|
+
// Option 1: Pass data to constructor, assume it accepts it
|
|
151
|
+
// const instance = new cls(data);
|
|
152
|
+
//
|
|
153
|
+
// Option 2: Skip constructor entirely, use Object.create
|
|
154
|
+
// const instance = Object.create(cls.prototype) as T;
|
|
155
|
+
// Object.assign(instance, data);
|
|
156
|
+
// // Gives prototype chain (methods work) without invoking constructor
|
|
157
|
+
//
|
|
158
|
+
// Option 3: Inspect constructor.length and try both
|
|
159
|
+
// if (cls.length > 0) {
|
|
160
|
+
// return new cls(data);
|
|
161
|
+
// }
|
|
162
|
+
// return Object.assign(new cls(), data);
|
|
163
|
+
/**
|
|
164
|
+
* Hydrate plain data into a class instance.
|
|
165
|
+
* Returns an instance with the class's prototype (so methods work).
|
|
166
|
+
*/
|
|
167
|
+
hydrate(data) {
|
|
168
|
+
const instance = new this.ResourceClass();
|
|
169
|
+
return Object.assign(instance, data);
|
|
170
|
+
}
|
|
171
|
+
// -------------------------------------------------------------------------
|
|
172
|
+
// CRUD+L methods - fully typed based on T
|
|
173
|
+
// -------------------------------------------------------------------------
|
|
174
|
+
/**
|
|
175
|
+
* Creates a new resource and returns it as a hydrated class instance.
|
|
176
|
+
*
|
|
177
|
+
* Injects standard platform field defaults (`created`, `updated`, `livemode`)
|
|
178
|
+
* before the caller's data, so the returned instance has the same shape a real
|
|
179
|
+
* Stripe backend would produce. Callers can override any default via `data`.
|
|
180
|
+
*
|
|
181
|
+
* @param data - Fields to set on the new resource (excluding `id` and `object`).
|
|
182
|
+
*/
|
|
183
|
+
async create(data) {
|
|
184
|
+
const timestamp = Date.now();
|
|
185
|
+
const withDefaults = {
|
|
186
|
+
created: new Date(timestamp),
|
|
187
|
+
updated: new Date(timestamp),
|
|
188
|
+
livemode: false,
|
|
189
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- CreateInput<T> is a generic mapped type (Partial<Omit<T,...>>) that TypeScript does not consider directly assignable to Record<string, unknown>; the cast is safe here because the Backend.create signature requires a plain record for the spread operation
|
|
190
|
+
...data
|
|
191
|
+
};
|
|
192
|
+
const result = await this.backend.create(this.objectType, withDefaults);
|
|
193
|
+
return this.hydrate(result);
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Retrieves a resource by ID and returns it as a hydrated class instance.
|
|
197
|
+
* @param id - The resource ID.
|
|
198
|
+
*/
|
|
199
|
+
async retrieve(id) {
|
|
200
|
+
const result = await this.backend.retrieve(this.objectType, id);
|
|
201
|
+
return this.hydrate(result);
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Merges the provided fields into an existing resource and returns the updated instance.
|
|
205
|
+
* @param id - The resource ID.
|
|
206
|
+
* @param data - Fields to merge into the existing resource (excluding `id` and `object`).
|
|
207
|
+
*/
|
|
208
|
+
async update(id, data) {
|
|
209
|
+
const result = await this.backend.update(
|
|
210
|
+
this.objectType,
|
|
211
|
+
id,
|
|
212
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- UpdateInput<T> is a generic mapped type (Partial<Omit<T,...>>) that TypeScript does not consider directly assignable to Record<string, unknown>; the cast is safe here because the Backend.update signature requires a plain record for the spread operation
|
|
213
|
+
data
|
|
214
|
+
);
|
|
215
|
+
return this.hydrate(result);
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Deletes a resource by ID.
|
|
219
|
+
* @param id - The resource ID.
|
|
220
|
+
*/
|
|
221
|
+
async delete(id) {
|
|
222
|
+
return this.backend.delete(this.objectType, id);
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Lists resources with optional pagination, returning hydrated class instances.
|
|
226
|
+
* @param params - Optional pagination parameters.
|
|
227
|
+
*/
|
|
228
|
+
async list(params) {
|
|
229
|
+
const result = await this.backend.list(this.objectType, params);
|
|
230
|
+
return {
|
|
231
|
+
...result,
|
|
232
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions, @typescript-eslint/no-unsafe-type-assertion -- each item is T from the backend; narrowing to Record<string, unknown> for Object.assign in hydrate is safe because T extends StripeResource which has only string-keyed properties
|
|
233
|
+
data: result.data.map((item) => this.hydrate(item))
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
// -------------------------------------------------------------------------
|
|
237
|
+
// Test utilities
|
|
238
|
+
// -------------------------------------------------------------------------
|
|
239
|
+
/** Clear all test data across all object types */
|
|
240
|
+
static clearAll() {
|
|
241
|
+
_TestClient.sharedBackend.clear();
|
|
242
|
+
}
|
|
243
|
+
};
|
|
244
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
245
|
+
0 && (module.exports = {
|
|
246
|
+
LocalBackend,
|
|
247
|
+
TestClient
|
|
248
|
+
});
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@stripe/extensibility-test-helpers`
|
|
3
|
+
*
|
|
4
|
+
* Generic test client for Stripe resources.
|
|
5
|
+
*
|
|
6
|
+
* Works with both CustomObject subclasses and API objects like Customer.
|
|
7
|
+
* Backend implementations can be swapped for local testing vs sandbox.
|
|
8
|
+
*
|
|
9
|
+
* @packageDocumentation
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Minimal interface for any Stripe resource.
|
|
13
|
+
* Both CustomObject and API objects satisfy this.
|
|
14
|
+
* @public
|
|
15
|
+
*/
|
|
16
|
+
export interface StripeResource {
|
|
17
|
+
/** Unique identifier for the resource. */
|
|
18
|
+
id: string;
|
|
19
|
+
/** String identifying the object type, e.g. `"customer"` or `"custom_object.order"`. */
|
|
20
|
+
object: string;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Input type for create - user-provided fields only.
|
|
24
|
+
*
|
|
25
|
+
* @public
|
|
26
|
+
*/
|
|
27
|
+
export type CreateInput<T extends StripeResource> = Partial<Omit<T, 'id' | 'object'>>;
|
|
28
|
+
/**
|
|
29
|
+
* Input type for update - partial user fields.
|
|
30
|
+
*
|
|
31
|
+
* @public
|
|
32
|
+
*/
|
|
33
|
+
export type UpdateInput<T extends StripeResource> = Partial<Omit<T, 'id' | 'object'>>;
|
|
34
|
+
/**
|
|
35
|
+
* List pagination params.
|
|
36
|
+
* @public
|
|
37
|
+
*/
|
|
38
|
+
export interface ListParams {
|
|
39
|
+
/** Maximum number of items to return. Defaults to `10`. */
|
|
40
|
+
limit?: number;
|
|
41
|
+
/** Return items after the item with this ID (exclusive). Use for forward pagination. */
|
|
42
|
+
startingAfter?: string;
|
|
43
|
+
/** Return items before the item with this ID (exclusive). Use for backward pagination. */
|
|
44
|
+
endingBefore?: string;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Paginated list response.
|
|
48
|
+
* @public
|
|
49
|
+
*/
|
|
50
|
+
export interface ListResponse<T> {
|
|
51
|
+
/** The items in the current page. */
|
|
52
|
+
data: T[];
|
|
53
|
+
/** Whether additional items exist beyond this page. */
|
|
54
|
+
hasMore: boolean;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* A class constructor that produces instances of T.
|
|
58
|
+
* @public
|
|
59
|
+
*/
|
|
60
|
+
export type ResourceClass<T extends StripeResource> = new (...args: unknown[]) => T;
|
|
61
|
+
/**
|
|
62
|
+
* Backend interface for storage/API operations.
|
|
63
|
+
* Implement for different environments (local, sandbox, production).
|
|
64
|
+
* @public
|
|
65
|
+
*/
|
|
66
|
+
export interface Backend {
|
|
67
|
+
/**
|
|
68
|
+
* Creates a new resource of the given object type.
|
|
69
|
+
* @param objectType - The Stripe object type string (e.g. `"customer"`).
|
|
70
|
+
* @param data - Fields to set on the new resource.
|
|
71
|
+
* @returns The created resource with a generated `id`.
|
|
72
|
+
*/
|
|
73
|
+
create<T extends StripeResource>(objectType: string, data: Record<string, unknown>): Promise<T>;
|
|
74
|
+
/**
|
|
75
|
+
* Retrieves a resource by ID.
|
|
76
|
+
* @param objectType - The Stripe object type string.
|
|
77
|
+
* @param id - The resource ID.
|
|
78
|
+
* @returns The matching resource.
|
|
79
|
+
*/
|
|
80
|
+
retrieve<T extends StripeResource>(objectType: string, id: string): Promise<T>;
|
|
81
|
+
/**
|
|
82
|
+
* Updates an existing resource by merging the provided fields.
|
|
83
|
+
* @param objectType - The Stripe object type string.
|
|
84
|
+
* @param id - The resource ID.
|
|
85
|
+
* @param data - Fields to merge into the existing resource.
|
|
86
|
+
* @returns The updated resource.
|
|
87
|
+
*/
|
|
88
|
+
update<T extends StripeResource>(objectType: string, id: string, data: Record<string, unknown>): Promise<T>;
|
|
89
|
+
/**
|
|
90
|
+
* Deletes a resource by ID.
|
|
91
|
+
* @param objectType - The Stripe object type string.
|
|
92
|
+
* @param id - The resource ID.
|
|
93
|
+
*/
|
|
94
|
+
delete(objectType: string, id: string): Promise<void>;
|
|
95
|
+
/**
|
|
96
|
+
* Lists resources of the given object type with optional pagination.
|
|
97
|
+
* @param objectType - The Stripe object type string.
|
|
98
|
+
* @param params - Optional pagination parameters.
|
|
99
|
+
* @returns A paginated list response.
|
|
100
|
+
*/
|
|
101
|
+
list<T extends StripeResource>(objectType: string, params?: ListParams): Promise<ListResponse<T>>;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* In-memory {@link Backend} implementation for use in unit tests.
|
|
105
|
+
*
|
|
106
|
+
* Each instance maintains its own isolated store. Use a shared instance
|
|
107
|
+
* when you need multiple clients to see the same data.
|
|
108
|
+
* @public
|
|
109
|
+
*/
|
|
110
|
+
export declare class LocalBackend implements Backend {
|
|
111
|
+
private store;
|
|
112
|
+
private idCounter;
|
|
113
|
+
private getCollection;
|
|
114
|
+
private generateId;
|
|
115
|
+
/** {@inheritDoc Backend.create} */
|
|
116
|
+
create<T extends StripeResource>(objectType: string, data: Record<string, unknown>): Promise<T>;
|
|
117
|
+
/** {@inheritDoc Backend.retrieve} */
|
|
118
|
+
retrieve<T extends StripeResource>(objectType: string, id: string): Promise<T>;
|
|
119
|
+
/** {@inheritDoc Backend.update} */
|
|
120
|
+
update<T extends StripeResource>(objectType: string, id: string, data: Record<string, unknown>): Promise<T>;
|
|
121
|
+
/** {@inheritDoc Backend.delete} */
|
|
122
|
+
delete(objectType: string, id: string): Promise<void>;
|
|
123
|
+
/** {@inheritDoc Backend.list} */
|
|
124
|
+
list<T extends StripeResource>(objectType: string, params?: ListParams): Promise<ListResponse<T>>;
|
|
125
|
+
/** Resets all stored objects and the ID counter. Useful in `beforeEach` hooks. */
|
|
126
|
+
clear(): void;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Options for constructing a {@link TestClient}.
|
|
130
|
+
* @public
|
|
131
|
+
*/
|
|
132
|
+
export interface TestClientOptions {
|
|
133
|
+
/** Explicit object type (if auto-derive doesn't work) */
|
|
134
|
+
objectType?: string;
|
|
135
|
+
/** Custom backend (defaults to shared LocalBackend) */
|
|
136
|
+
backend?: Backend;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Generic test client for any Stripe resource.
|
|
140
|
+
*
|
|
141
|
+
* @example
|
|
142
|
+
* ```ts
|
|
143
|
+
* const locations = new TestClient(Location);
|
|
144
|
+
* const customers = new TestClient(Customer);
|
|
145
|
+
*
|
|
146
|
+
* const loc = await locations.create({ address: '123 Main' });
|
|
147
|
+
* const cust = await customers.create({ email: 'test@example.com' });
|
|
148
|
+
* ```
|
|
149
|
+
* @public
|
|
150
|
+
*/
|
|
151
|
+
export declare class TestClient<T extends StripeResource> {
|
|
152
|
+
private ResourceClass;
|
|
153
|
+
private static sharedBackend;
|
|
154
|
+
private objectType;
|
|
155
|
+
private backend;
|
|
156
|
+
constructor(ResourceClass: ResourceClass<T>, options?: TestClientOptions);
|
|
157
|
+
/**
|
|
158
|
+
* Derive object type by instantiating the class and reading its `object` field.
|
|
159
|
+
*
|
|
160
|
+
* This works for classes that set `object` in their constructor or as a field
|
|
161
|
+
* initializer (e.g. `object = 'test.resource'`). It does **not** work for
|
|
162
|
+
* custom object classes that extend `BaseObject`, because `BaseObject.object`
|
|
163
|
+
* is only set after the build-time code transform runs — plain construction
|
|
164
|
+
* leaves it undefined. For those classes, pass `objectType` explicitly in the
|
|
165
|
+
* constructor options.
|
|
166
|
+
*/
|
|
167
|
+
private deriveObjectType;
|
|
168
|
+
/**
|
|
169
|
+
* Hydrate plain data into a class instance.
|
|
170
|
+
* Returns an instance with the class's prototype (so methods work).
|
|
171
|
+
*/
|
|
172
|
+
private hydrate;
|
|
173
|
+
/**
|
|
174
|
+
* Creates a new resource and returns it as a hydrated class instance.
|
|
175
|
+
*
|
|
176
|
+
* Injects standard platform field defaults (`created`, `updated`, `livemode`)
|
|
177
|
+
* before the caller's data, so the returned instance has the same shape a real
|
|
178
|
+
* Stripe backend would produce. Callers can override any default via `data`.
|
|
179
|
+
*
|
|
180
|
+
* @param data - Fields to set on the new resource (excluding `id` and `object`).
|
|
181
|
+
*/
|
|
182
|
+
create(data: CreateInput<T>): Promise<T>;
|
|
183
|
+
/**
|
|
184
|
+
* Retrieves a resource by ID and returns it as a hydrated class instance.
|
|
185
|
+
* @param id - The resource ID.
|
|
186
|
+
*/
|
|
187
|
+
retrieve(id: string): Promise<T>;
|
|
188
|
+
/**
|
|
189
|
+
* Merges the provided fields into an existing resource and returns the updated instance.
|
|
190
|
+
* @param id - The resource ID.
|
|
191
|
+
* @param data - Fields to merge into the existing resource (excluding `id` and `object`).
|
|
192
|
+
*/
|
|
193
|
+
update(id: string, data: UpdateInput<T>): Promise<T>;
|
|
194
|
+
/**
|
|
195
|
+
* Deletes a resource by ID.
|
|
196
|
+
* @param id - The resource ID.
|
|
197
|
+
*/
|
|
198
|
+
delete(id: string): Promise<void>;
|
|
199
|
+
/**
|
|
200
|
+
* Lists resources with optional pagination, returning hydrated class instances.
|
|
201
|
+
* @param params - Optional pagination parameters.
|
|
202
|
+
*/
|
|
203
|
+
list(params?: ListParams): Promise<ListResponse<T>>;
|
|
204
|
+
/** Clear all test data across all object types */
|
|
205
|
+
static clearAll(): void;
|
|
206
|
+
}
|
|
207
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAMH;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC7B,0CAA0C;IAC1C,EAAE,EAAE,MAAM,CAAC;IACX,wFAAwF;IACxF,MAAM,EAAE,MAAM,CAAC;CAChB;AAMD;;;;GAIG;AACH,MAAM,MAAM,WAAW,CAAC,CAAC,SAAS,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,EAAE,IAAI,GAAG,QAAQ,CAAC,CAAC,CAAC;AAEtF;;;;GAIG;AACH,MAAM,MAAM,WAAW,CAAC,CAAC,SAAS,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,EAAE,IAAI,GAAG,QAAQ,CAAC,CAAC,CAAC;AAEtF;;;GAGG;AACH,MAAM,WAAW,UAAU;IACzB,2DAA2D;IAC3D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,wFAAwF;IACxF,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,0FAA0F;IAC1F,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;GAGG;AACH,MAAM,WAAW,YAAY,CAAC,CAAC;IAC7B,qCAAqC;IACrC,IAAI,EAAE,CAAC,EAAE,CAAC;IACV,uDAAuD;IACvD,OAAO,EAAE,OAAO,CAAC;CAClB;AAMD;;;GAGG;AACH,MAAM,MAAM,aAAa,CAAC,CAAC,SAAS,cAAc,IAAI,KAAK,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC;AAMpF;;;;GAIG;AACH,MAAM,WAAW,OAAO;IACtB;;;;;OAKG;IACH,MAAM,CAAC,CAAC,SAAS,cAAc,EAC7B,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,OAAO,CAAC,CAAC,CAAC,CAAC;IAEd;;;;;OAKG;IACH,QAAQ,CAAC,CAAC,SAAS,cAAc,EAAE,UAAU,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IAE/E;;;;;;OAMG;IACH,MAAM,CAAC,CAAC,SAAS,cAAc,EAC7B,UAAU,EAAE,MAAM,EAClB,EAAE,EAAE,MAAM,EACV,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,OAAO,CAAC,CAAC,CAAC,CAAC;IAEd;;;;OAIG;IACH,MAAM,CAAC,UAAU,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEtD;;;;;OAKG;IACH,IAAI,CAAC,CAAC,SAAS,cAAc,EAC3B,UAAU,EAAE,MAAM,EAClB,MAAM,CAAC,EAAE,UAAU,GAClB,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC;CAC7B;AAMD;;;;;;GAMG;AACH,qBAAa,YAAa,YAAW,OAAO;IAC1C,OAAO,CAAC,KAAK,CAAkD;IAC/D,OAAO,CAAC,SAAS,CAAK;IAEtB,OAAO,CAAC,aAAa;IAQrB,OAAO,CAAC,UAAU;IAIlB,mCAAmC;IACnC,MAAM,CAAC,CAAC,SAAS,cAAc,EAC7B,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,OAAO,CAAC,CAAC,CAAC;IAYb,qCAAqC;IACrC,QAAQ,CAAC,CAAC,SAAS,cAAc,EAAE,UAAU,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC;IAS9E,mCAAmC;IACnC,MAAM,CAAC,CAAC,SAAS,cAAc,EAC7B,UAAU,EAAE,MAAM,EAClB,EAAE,EAAE,MAAM,EACV,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,OAAO,CAAC,CAAC,CAAC;IAWb,mCAAmC;IACnC,MAAM,CAAC,UAAU,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IASrD,iCAAiC;IACjC,IAAI,CAAC,CAAC,SAAS,cAAc,EAC3B,UAAU,EAAE,MAAM,EAClB,MAAM,CAAC,EAAE,UAAU,GAClB,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IAkC3B,kFAAkF;IAClF,KAAK,IAAI,IAAI;CAId;AAMD;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAChC,yDAAyD;IACzD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,uDAAuD;IACvD,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;;;;;;;;;;;GAYG;AACH,qBAAa,UAAU,CAAC,CAAC,SAAS,cAAc;IAO5C,OAAO,CAAC,aAAa;IANvB,OAAO,CAAC,MAAM,CAAC,aAAa,CAAsB;IAElD,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,OAAO,CAAU;gBAGf,aAAa,EAAE,aAAa,CAAC,CAAC,CAAC,EACvC,OAAO,CAAC,EAAE,iBAAiB;IAM7B;;;;;;;;;OASG;IACH,OAAO,CAAC,gBAAgB;IAwCxB;;;OAGG;IACH,OAAO,CAAC,OAAO;IASf;;;;;;;;OAQG;IACG,MAAM,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAc9C;;;OAGG;IACG,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC;IAMtC;;;;OAIG;IACG,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAW1D;;;OAGG;IACG,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIvC;;;OAGG;IACG,IAAI,CAAC,MAAM,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IAazD,kDAAkD;IAClD,MAAM,CAAC,QAAQ,IAAI,IAAI;CAGxB"}
|