@dangao/bun-server 3.2.0 → 3.3.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/dist/ai/types.d.ts +4 -0
- package/dist/ai/types.d.ts.map +1 -1
- package/dist/auth/types.d.ts +4 -0
- package/dist/auth/types.d.ts.map +1 -1
- package/dist/cache/interceptors.d.ts +3 -3
- package/dist/cache/interceptors.d.ts.map +1 -1
- package/dist/cache/service.d.ts +6 -6
- package/dist/cache/service.d.ts.map +1 -1
- package/dist/cache/types.d.ts +12 -12
- package/dist/cache/types.d.ts.map +1 -1
- package/dist/config/service.d.ts +6 -4
- package/dist/config/service.d.ts.map +1 -1
- package/dist/core/application.d.ts +4 -0
- package/dist/core/application.d.ts.map +1 -1
- package/dist/core/context.d.ts +4 -2
- package/dist/core/context.d.ts.map +1 -1
- package/dist/database/sqlite-adapter.d.ts +4 -2
- package/dist/database/sqlite-adapter.d.ts.map +1 -1
- package/dist/di/container.d.ts +2 -0
- package/dist/di/container.d.ts.map +1 -1
- package/dist/di/module-registry.d.ts.map +1 -1
- package/dist/di/module.d.ts +11 -1
- package/dist/di/module.d.ts.map +1 -1
- package/dist/di/types.d.ts +1 -1
- package/dist/di/types.d.ts.map +1 -1
- package/dist/error/handler.d.ts.map +1 -1
- package/dist/error/http-exception.d.ts +8 -8
- package/dist/error/http-exception.d.ts.map +1 -1
- package/dist/error/index.d.ts +1 -0
- package/dist/error/index.d.ts.map +1 -1
- package/dist/events/service.d.ts +2 -2
- package/dist/events/service.d.ts.map +1 -1
- package/dist/events/types.d.ts +12 -3
- package/dist/events/types.d.ts.map +1 -1
- package/dist/index.js +63 -25
- package/dist/index.node.mjs +63 -25
- package/dist/interceptor/base-interceptor.d.ts +3 -3
- package/dist/interceptor/base-interceptor.d.ts.map +1 -1
- package/dist/interceptor/builtin/log-interceptor.d.ts +1 -1
- package/dist/interceptor/builtin/log-interceptor.d.ts.map +1 -1
- package/dist/interceptor/builtin/permission-interceptor.d.ts +1 -1
- package/dist/interceptor/builtin/permission-interceptor.d.ts.map +1 -1
- package/dist/interceptor/interceptor-chain.d.ts +1 -1
- package/dist/interceptor/interceptor-chain.d.ts.map +1 -1
- package/dist/interceptor/interceptor-registry.d.ts +3 -1
- package/dist/interceptor/interceptor-registry.d.ts.map +1 -1
- package/dist/interceptor/types.d.ts +6 -1
- package/dist/interceptor/types.d.ts.map +1 -1
- package/dist/microservice/service-client/types.d.ts +1 -0
- package/dist/microservice/service-client/types.d.ts.map +1 -1
- package/dist/microservice/tracing/tracer.d.ts +1 -0
- package/dist/microservice/tracing/tracer.d.ts.map +1 -1
- package/dist/middleware/builtin/file-upload.d.ts +2 -0
- package/dist/middleware/builtin/file-upload.d.ts.map +1 -1
- package/dist/middleware/builtin/rate-limit.d.ts +9 -1
- package/dist/middleware/builtin/rate-limit.d.ts.map +1 -1
- package/dist/queue/service.d.ts +2 -2
- package/dist/queue/service.d.ts.map +1 -1
- package/dist/queue/types.d.ts +25 -1
- package/dist/queue/types.d.ts.map +1 -1
- package/dist/router/decorators.d.ts +1 -2
- package/dist/router/decorators.d.ts.map +1 -1
- package/dist/security/guards/types.d.ts +1 -0
- package/dist/security/guards/types.d.ts.map +1 -1
- package/dist/security/types.d.ts +1 -1
- package/dist/security/types.d.ts.map +1 -1
- package/dist/session/types.d.ts +8 -0
- package/dist/session/types.d.ts.map +1 -1
- package/dist/swagger/decorators.d.ts +1 -1
- package/dist/swagger/decorators.d.ts.map +1 -1
- package/dist/swagger/types.d.ts +1 -1
- package/dist/swagger/types.d.ts.map +1 -1
- package/dist/testing/harness.d.ts +1 -1
- package/dist/testing/harness.d.ts.map +1 -1
- package/dist/testing/test-client.d.ts +1 -1
- package/dist/testing/test-client.d.ts.map +1 -1
- package/dist/testing/testing-module.d.ts +2 -2
- package/dist/testing/testing-module.d.ts.map +1 -1
- package/dist/validation/errors.d.ts +5 -1
- package/dist/validation/errors.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/ai/types.ts +5 -0
- package/src/auth/types.ts +4 -1
- package/src/cache/interceptors.ts +6 -6
- package/src/cache/service-proxy.ts +2 -2
- package/src/cache/service.ts +17 -8
- package/src/cache/types.ts +12 -12
- package/src/config/service.ts +8 -6
- package/src/core/application.ts +7 -1
- package/src/core/context.ts +6 -3
- package/src/database/sqlite-adapter.ts +4 -3
- package/src/di/container.ts +13 -0
- package/src/di/module-registry.ts +2 -3
- package/src/di/module.ts +21 -2
- package/src/di/types.ts +1 -2
- package/src/error/handler.ts +3 -4
- package/src/error/http-exception.ts +11 -11
- package/src/error/index.ts +1 -1
- package/src/events/service.ts +4 -2
- package/src/events/types.ts +14 -3
- package/src/interceptor/base-interceptor.ts +5 -6
- package/src/interceptor/builtin/log-interceptor.ts +2 -3
- package/src/interceptor/builtin/permission-interceptor.ts +2 -3
- package/src/interceptor/interceptor-chain.ts +6 -7
- package/src/interceptor/interceptor-registry.ts +5 -3
- package/src/interceptor/types.ts +9 -4
- package/src/microservice/service-client/types.ts +1 -1
- package/src/microservice/tracing/tracer.ts +15 -3
- package/src/middleware/builtin/file-upload.ts +3 -2
- package/src/middleware/builtin/rate-limit.ts +22 -5
- package/src/queue/service.ts +1 -1
- package/src/queue/types.ts +40 -1
- package/src/router/decorators.ts +2 -3
- package/src/security/guards/types.ts +2 -1
- package/src/security/types.ts +1 -2
- package/src/session/service.ts +1 -1
- package/src/session/types.ts +10 -0
- package/src/swagger/decorators.ts +15 -4
- package/src/swagger/generator.ts +2 -3
- package/src/swagger/types.ts +1 -2
- package/src/testing/harness.ts +1 -2
- package/src/testing/test-client.ts +2 -2
- package/src/testing/testing-module.ts +5 -5
- package/src/validation/errors.ts +6 -2
- package/tests/bun-test-shim.d.ts +11 -0
- package/tests/controller/context-decorator.test.ts +1 -2
- package/tests/di/module.test.ts +199 -1
- package/tests/events/event-emitter.test.ts +2 -2
- package/tests/global.d.ts +30 -0
- package/tests/queue/queue-service.test.ts +14 -0
- package/tests/testing/testing-module.test.ts +20 -0
|
@@ -155,7 +155,7 @@ describe('@Context() Decorator', () => {
|
|
|
155
155
|
// 这个测试验证 ContextService 可以在服务层使用
|
|
156
156
|
const { ContextService, CONTEXT_SERVICE_TOKEN } = await import('../../src/core/context-service');
|
|
157
157
|
const container = app.getContainer();
|
|
158
|
-
const contextService = container.resolve<ContextService
|
|
158
|
+
const contextService = container.resolve<InstanceType<typeof ContextService>>(CONTEXT_SERVICE_TOKEN);
|
|
159
159
|
|
|
160
160
|
@Controller('/api/test')
|
|
161
161
|
class TestController {
|
|
@@ -180,4 +180,3 @@ describe('@Context() Decorator', () => {
|
|
|
180
180
|
expect(data.method).toBe('GET');
|
|
181
181
|
});
|
|
182
182
|
});
|
|
183
|
-
|
package/tests/di/module.test.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { Controller, ControllerRegistry } from '../../src/controller/controller'
|
|
|
5
5
|
import { GET } from '../../src/router/decorators';
|
|
6
6
|
import { Module } from '../../src/di/module';
|
|
7
7
|
import { ModuleRegistry } from '../../src/di/module-registry';
|
|
8
|
+
import { Container } from '../../src/di/container';
|
|
8
9
|
import { RouteRegistry } from '../../src/router/registry';
|
|
9
10
|
import { Injectable, Inject } from '../../src/di/decorators';
|
|
10
11
|
import { Context } from '../../src/core/context';
|
|
@@ -177,6 +178,204 @@ describe('ModuleRegistry', () => {
|
|
|
177
178
|
expect(() => ref.container.resolve(ALIAS)).toThrow(/Provider not found/);
|
|
178
179
|
});
|
|
179
180
|
|
|
181
|
+
test('FactoryProvider inject passes resolved dependencies to useFactory', () => {
|
|
182
|
+
const TOKEN = Symbol.for('test.factory.inject.basic');
|
|
183
|
+
|
|
184
|
+
@Injectable()
|
|
185
|
+
class Dep {
|
|
186
|
+
public greet(): string {
|
|
187
|
+
return 'hello';
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
@Module({
|
|
192
|
+
providers: [
|
|
193
|
+
Dep,
|
|
194
|
+
{
|
|
195
|
+
provide: TOKEN,
|
|
196
|
+
useFactory: (dep: Dep) => ({
|
|
197
|
+
dep,
|
|
198
|
+
message: dep.greet(),
|
|
199
|
+
}),
|
|
200
|
+
inject: [Dep],
|
|
201
|
+
},
|
|
202
|
+
],
|
|
203
|
+
})
|
|
204
|
+
class TestModule {}
|
|
205
|
+
|
|
206
|
+
const app = new Application();
|
|
207
|
+
app.registerModule(TestModule);
|
|
208
|
+
|
|
209
|
+
const ref = ModuleRegistry.getInstance().getModuleRef(TestModule)!;
|
|
210
|
+
const dep = ref.container.resolve(Dep);
|
|
211
|
+
const result = ref.container.resolve<{ dep: Dep; message: string }>(TOKEN);
|
|
212
|
+
|
|
213
|
+
expect(result.dep).toBe(dep);
|
|
214
|
+
expect(result.message).toBe('hello');
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
test('FactoryProvider without inject or with empty inject still passes Container to useFactory', () => {
|
|
218
|
+
const TOKEN = Symbol.for('test.factory.inject.compat');
|
|
219
|
+
const EMPTY_INJECT_TOKEN = Symbol.for('test.factory.inject.compat.empty');
|
|
220
|
+
|
|
221
|
+
@Injectable()
|
|
222
|
+
class Dep {
|
|
223
|
+
public readonly value = 'manual';
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
@Module({
|
|
227
|
+
providers: [
|
|
228
|
+
Dep,
|
|
229
|
+
{
|
|
230
|
+
provide: TOKEN,
|
|
231
|
+
useFactory: (container: Container) => ({
|
|
232
|
+
isContainer: container instanceof Container,
|
|
233
|
+
dep: container.resolve(Dep),
|
|
234
|
+
}),
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
provide: EMPTY_INJECT_TOKEN,
|
|
238
|
+
useFactory: (container: Container) => ({
|
|
239
|
+
isContainer: container instanceof Container,
|
|
240
|
+
dep: container.resolve(Dep),
|
|
241
|
+
}),
|
|
242
|
+
inject: [],
|
|
243
|
+
},
|
|
244
|
+
],
|
|
245
|
+
})
|
|
246
|
+
class TestModule {}
|
|
247
|
+
|
|
248
|
+
const app = new Application();
|
|
249
|
+
app.registerModule(TestModule);
|
|
250
|
+
|
|
251
|
+
const ref = ModuleRegistry.getInstance().getModuleRef(TestModule)!;
|
|
252
|
+
const dep = ref.container.resolve(Dep);
|
|
253
|
+
const result = ref.container.resolve<{ isContainer: boolean; dep: Dep }>(TOKEN);
|
|
254
|
+
const emptyInjectResult = ref.container.resolve<{ isContainer: boolean; dep: Dep }>(
|
|
255
|
+
EMPTY_INJECT_TOKEN,
|
|
256
|
+
);
|
|
257
|
+
|
|
258
|
+
expect(result.isContainer).toBe(true);
|
|
259
|
+
expect(result.dep).toBe(dep);
|
|
260
|
+
expect(emptyInjectResult.isContainer).toBe(true);
|
|
261
|
+
expect(emptyInjectResult.dep).toBe(dep);
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
test('FactoryProvider inject resolves tokens exported from imported modules', () => {
|
|
265
|
+
const DEP_TOKEN = Symbol.for('test.factory.inject.exported.dep');
|
|
266
|
+
const TOKEN = Symbol.for('test.factory.inject.exported.result');
|
|
267
|
+
|
|
268
|
+
interface DepContract {
|
|
269
|
+
label(): string;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
const exportedDep: DepContract = {
|
|
273
|
+
label: () => 'from-export',
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
@Module({
|
|
277
|
+
providers: [{ provide: DEP_TOKEN, useValue: exportedDep }],
|
|
278
|
+
exports: [DEP_TOKEN],
|
|
279
|
+
})
|
|
280
|
+
class SharedModule {}
|
|
281
|
+
|
|
282
|
+
@Module({
|
|
283
|
+
imports: [SharedModule],
|
|
284
|
+
providers: [
|
|
285
|
+
{
|
|
286
|
+
provide: TOKEN,
|
|
287
|
+
useFactory: (dep: DepContract) => ({
|
|
288
|
+
dep,
|
|
289
|
+
label: dep.label(),
|
|
290
|
+
}),
|
|
291
|
+
inject: [DEP_TOKEN],
|
|
292
|
+
},
|
|
293
|
+
],
|
|
294
|
+
})
|
|
295
|
+
class AppModule {}
|
|
296
|
+
|
|
297
|
+
const app = new Application();
|
|
298
|
+
app.registerModule(AppModule);
|
|
299
|
+
|
|
300
|
+
const ref = ModuleRegistry.getInstance().getModuleRef(AppModule)!;
|
|
301
|
+
const result = ref.container.resolve<{ dep: DepContract; label: string }>(TOKEN);
|
|
302
|
+
|
|
303
|
+
expect(result.dep).toBe(exportedDep);
|
|
304
|
+
expect(result.label).toBe('from-export');
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
test('FactoryProvider inject preserves token order for factory arguments', () => {
|
|
308
|
+
const FIRST = Symbol.for('test.factory.inject.order.first');
|
|
309
|
+
const SECOND = Symbol.for('test.factory.inject.order.second');
|
|
310
|
+
const TOKEN = Symbol.for('test.factory.inject.order.result');
|
|
311
|
+
|
|
312
|
+
@Module({
|
|
313
|
+
providers: [
|
|
314
|
+
{ provide: FIRST, useValue: 'first' },
|
|
315
|
+
{ provide: SECOND, useValue: 'second' },
|
|
316
|
+
{
|
|
317
|
+
provide: TOKEN,
|
|
318
|
+
useFactory: (second: string, first: string) => [second, first],
|
|
319
|
+
inject: [SECOND, FIRST],
|
|
320
|
+
},
|
|
321
|
+
],
|
|
322
|
+
})
|
|
323
|
+
class TestModule {}
|
|
324
|
+
|
|
325
|
+
const app = new Application();
|
|
326
|
+
app.registerModule(TestModule);
|
|
327
|
+
|
|
328
|
+
const ref = ModuleRegistry.getInstance().getModuleRef(TestModule)!;
|
|
329
|
+
const result = ref.container.resolve<string[]>(TOKEN);
|
|
330
|
+
|
|
331
|
+
expect(result).toEqual(['second', 'first']);
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
test('FactoryProvider inject works when mixed with useExisting useValue and useClass providers', () => {
|
|
335
|
+
const CLASS_TOKEN = Symbol.for('test.factory.inject.mix.class');
|
|
336
|
+
const VALUE_TOKEN = Symbol.for('test.factory.inject.mix.value');
|
|
337
|
+
const ALIAS_TOKEN = Symbol.for('test.factory.inject.mix.alias');
|
|
338
|
+
const TOKEN = Symbol.for('test.factory.inject.mix.result');
|
|
339
|
+
|
|
340
|
+
class Impl {
|
|
341
|
+
public readonly kind = 'impl';
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
const value = { kind: 'value' };
|
|
345
|
+
|
|
346
|
+
@Module({
|
|
347
|
+
providers: [
|
|
348
|
+
{ provide: CLASS_TOKEN, useClass: Impl },
|
|
349
|
+
{ provide: VALUE_TOKEN, useValue: value },
|
|
350
|
+
{ provide: ALIAS_TOKEN, useExisting: CLASS_TOKEN },
|
|
351
|
+
{
|
|
352
|
+
provide: TOKEN,
|
|
353
|
+
useFactory: (aliased: Impl, injectedValue: typeof value) => ({
|
|
354
|
+
aliased,
|
|
355
|
+
injectedValue,
|
|
356
|
+
}),
|
|
357
|
+
inject: [ALIAS_TOKEN, VALUE_TOKEN],
|
|
358
|
+
},
|
|
359
|
+
],
|
|
360
|
+
})
|
|
361
|
+
class TestModule {}
|
|
362
|
+
|
|
363
|
+
const app = new Application();
|
|
364
|
+
app.registerModule(TestModule);
|
|
365
|
+
|
|
366
|
+
const ref = ModuleRegistry.getInstance().getModuleRef(TestModule)!;
|
|
367
|
+
const classInstance = ref.container.resolve<Impl>(CLASS_TOKEN);
|
|
368
|
+
const aliasInstance = ref.container.resolve<Impl>(ALIAS_TOKEN);
|
|
369
|
+
const valueInstance = ref.container.resolve<typeof value>(VALUE_TOKEN);
|
|
370
|
+
const result = ref.container.resolve<{ aliased: Impl; injectedValue: typeof value }>(TOKEN);
|
|
371
|
+
|
|
372
|
+
expect(classInstance).toBeInstanceOf(Impl);
|
|
373
|
+
expect(aliasInstance).toBe(classInstance);
|
|
374
|
+
expect(valueInstance).toBe(value);
|
|
375
|
+
expect(result.aliased).toBe(classInstance);
|
|
376
|
+
expect(result.injectedValue).toBe(value);
|
|
377
|
+
});
|
|
378
|
+
|
|
180
379
|
test('should throw error for circular module dependencies', () => {
|
|
181
380
|
@Module({
|
|
182
381
|
imports: [],
|
|
@@ -199,4 +398,3 @@ describe('ModuleRegistry', () => {
|
|
|
199
398
|
);
|
|
200
399
|
});
|
|
201
400
|
});
|
|
202
|
-
|
|
@@ -257,7 +257,7 @@ describe('EventEmitterService', () => {
|
|
|
257
257
|
});
|
|
258
258
|
|
|
259
259
|
test('should use custom error handler when provided', () => {
|
|
260
|
-
const errorHandler = mock(() => {});
|
|
260
|
+
const errorHandler = mock((_error: Error) => {});
|
|
261
261
|
const emitterWithErrorHandler = new EventEmitterService({
|
|
262
262
|
onError: errorHandler,
|
|
263
263
|
});
|
|
@@ -274,7 +274,7 @@ describe('EventEmitterService', () => {
|
|
|
274
274
|
|
|
275
275
|
describe('maxListeners warning', () => {
|
|
276
276
|
test('should warn when exceeding maxListeners', () => {
|
|
277
|
-
const consoleSpy = mock(() => {});
|
|
277
|
+
const consoleSpy = mock((_message: string) => {});
|
|
278
278
|
const originalWarn = console.warn;
|
|
279
279
|
console.warn = consoleSpy;
|
|
280
280
|
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
declare global {
|
|
2
|
+
interface Body {
|
|
3
|
+
json(): Promise<any>;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
interface Response {
|
|
7
|
+
json(): Promise<any>;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
declare module 'bun:test' {
|
|
12
|
+
export function expect(actual?: any): any;
|
|
13
|
+
export function mock(fn?: (...args: any[]) => any): any;
|
|
14
|
+
|
|
15
|
+
interface MatchersBuiltin<T = unknown> {
|
|
16
|
+
toBe(expected: any): void;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
interface Matchers<T = unknown> {
|
|
20
|
+
toBe(expected: any): void;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
declare module '@dangao/logsmith' {
|
|
25
|
+
interface LogEntry {
|
|
26
|
+
data?: unknown;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export {};
|
|
@@ -47,6 +47,16 @@ function createMockStore(): QueueStore {
|
|
|
47
47
|
return first as Job<T> | undefined;
|
|
48
48
|
},
|
|
49
49
|
|
|
50
|
+
async updateStatus(queueName: string, jobId: string, status: Job['status']): Promise<boolean> {
|
|
51
|
+
const job = queues.get(queueName)?.get(jobId);
|
|
52
|
+
if (!job) {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
job.status = status;
|
|
56
|
+
job.updatedAt = Date.now();
|
|
57
|
+
return true;
|
|
58
|
+
},
|
|
59
|
+
|
|
50
60
|
async complete(queueName: string, jobId: string, result?: any): Promise<boolean> {
|
|
51
61
|
const job = queues.get(queueName)?.get(jobId);
|
|
52
62
|
if (job) {
|
|
@@ -84,6 +94,10 @@ function createMockStore(): QueueStore {
|
|
|
84
94
|
}
|
|
85
95
|
return { waiting, active, completed, failed };
|
|
86
96
|
},
|
|
97
|
+
|
|
98
|
+
async count(queueName: string): Promise<number> {
|
|
99
|
+
return queues.get(queueName)?.size ?? 0;
|
|
100
|
+
},
|
|
87
101
|
};
|
|
88
102
|
}
|
|
89
103
|
|
|
@@ -126,4 +126,24 @@ describe('TestingModule', () => {
|
|
|
126
126
|
const greeter = module.get<Greeter>(GREETER_TOKEN);
|
|
127
127
|
expect(greeter.greet('X')).toBe('Factory: X');
|
|
128
128
|
});
|
|
129
|
+
|
|
130
|
+
test('should inject dependencies into provider factories', async () => {
|
|
131
|
+
const FACTORY_TOKEN = Symbol('FactoryGreeter');
|
|
132
|
+
|
|
133
|
+
const module = await Test.createTestingModule({
|
|
134
|
+
providers: [
|
|
135
|
+
{ provide: GREETER_TOKEN, useClass: RealGreeter },
|
|
136
|
+
{
|
|
137
|
+
provide: FACTORY_TOKEN,
|
|
138
|
+
useFactory: (greeter: Greeter) => ({
|
|
139
|
+
greet: (name: string) => greeter.greet(name).toUpperCase(),
|
|
140
|
+
}),
|
|
141
|
+
inject: [GREETER_TOKEN],
|
|
142
|
+
},
|
|
143
|
+
],
|
|
144
|
+
}).compile();
|
|
145
|
+
|
|
146
|
+
const greeter = module.get<Greeter>(FACTORY_TOKEN);
|
|
147
|
+
expect(greeter.greet('Test')).toBe('HELLO, TEST!');
|
|
148
|
+
});
|
|
129
149
|
});
|