From c65ad0d9a41a4cea2b8ce01bb758a2782dca6c45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roberto=20Pintos=20L=C3=B3pez?= Date: Tue, 7 Jan 2025 00:16:52 +0100 Subject: [PATCH 1/7] chore: ignore ts build metadata --- packages/container/tools/e2e-tests/.gitignore | 2 +- packages/container/tools/e2e-tests/tsconfig.cjs.tsbuildinfo | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) delete mode 100644 packages/container/tools/e2e-tests/tsconfig.cjs.tsbuildinfo diff --git a/packages/container/tools/e2e-tests/.gitignore b/packages/container/tools/e2e-tests/.gitignore index 435ed73e..953108be 100644 --- a/packages/container/tools/e2e-tests/.gitignore +++ b/packages/container/tools/e2e-tests/.gitignore @@ -1,7 +1,7 @@ # Typescript compiled files /lib/** -/tsconfig.tsbuildinfo +/tsconfig.cjs.tsbuildinfo # node modules /node_modules/ diff --git a/packages/container/tools/e2e-tests/tsconfig.cjs.tsbuildinfo b/packages/container/tools/e2e-tests/tsconfig.cjs.tsbuildinfo deleted file mode 100644 index cffcfdd3..00000000 --- a/packages/container/tools/e2e-tests/tsconfig.cjs.tsbuildinfo +++ /dev/null @@ -1 +0,0 @@ -{"root":["./src/app/actions/initializeWorld.ts","./src/app/hooks/before.ts","./src/binding/actions/setBinding.ts","./src/binding/calculations/getContainerOrFail.ts","./src/binding/models/BaseBindingParameter.ts","./src/binding/models/BindingParameter.ts","./src/binding/models/BindingParameterKind.ts","./src/binding/models/ConstantValueBindingParameter.ts","./src/binding/step-definitions/givenDefinitions.ts","./src/binding/step-definitions/thenDefinitions.ts","./src/binding/step-definitions/whenDefinitions.ts","./src/common/models/InversifyWorld.ts","./src/common/models/Writable.ts","./src/common/models/defaultAlias.ts","./src/container/actions/setContainer.ts","./src/container/calculations/getContainerOrFail.ts","./src/container/step-definitions/givenDefinitions.ts"],"version":"5.7.2"} \ No newline at end of file From 2a34ed1afaea46823f144bb0f88ef67ba54d3ea1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roberto=20Pintos=20L=C3=B3pez?= Date: Tue, 7 Jan 2025 00:17:27 +0100 Subject: [PATCH 2/7] chore(e2e-tests): update config to import parameter files --- .../tools/e2e-tests/config/cucumber.cjs.config.mjs | 6 +++++- .../container/tools/e2e-tests/config/cucumber.ts.config.mjs | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/packages/container/tools/e2e-tests/config/cucumber.cjs.config.mjs b/packages/container/tools/e2e-tests/config/cucumber.cjs.config.mjs index e63e483b..f1500423 100644 --- a/packages/container/tools/e2e-tests/config/cucumber.cjs.config.mjs +++ b/packages/container/tools/e2e-tests/config/cucumber.cjs.config.mjs @@ -8,7 +8,11 @@ function getConfiguration(parallel) { /** @type {!import("@cucumber/cucumber/lib/configuration").IConfiguration} */ const config = { ...getBaseConfiguration(parallel), - require: ['lib/cjs/*/step-definitions/*.js', 'lib/cjs/app/hooks/*.js'], + require: [ + 'lib/cjs/*/parameters/*.js', + 'lib/cjs/*/step-definitions/*.js', + 'lib/cjs/app/hooks/*.js', + ], }; return config; diff --git a/packages/container/tools/e2e-tests/config/cucumber.ts.config.mjs b/packages/container/tools/e2e-tests/config/cucumber.ts.config.mjs index 50aa63d8..ae4f5cb4 100644 --- a/packages/container/tools/e2e-tests/config/cucumber.ts.config.mjs +++ b/packages/container/tools/e2e-tests/config/cucumber.ts.config.mjs @@ -8,7 +8,11 @@ function getConfiguration(parallel) { /** @type {!import("@cucumber/cucumber/lib/configuration").IConfiguration} */ const config = { ...getBaseConfiguration(parallel), - require: ['src/*/step-definitions/*.ts', 'src/app/hooks/*.ts'], + require: [ + 'src/*/parameters/*.ts', + 'src/*/step-definitions/*.ts', + 'src/app/hooks/*.ts', + ], requireModule: ['ts-node/register'], }; From 2abc33fabda5d4a083e30247bbc03621434fb190 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roberto=20Pintos=20L=C3=B3pez?= Date: Tue, 7 Jan 2025 00:17:47 +0100 Subject: [PATCH 3/7] test(e2e-tests): add stringList parameter --- .../e2e-tests/src/common/parameters/stringList.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 packages/container/tools/e2e-tests/src/common/parameters/stringList.ts diff --git a/packages/container/tools/e2e-tests/src/common/parameters/stringList.ts b/packages/container/tools/e2e-tests/src/common/parameters/stringList.ts new file mode 100644 index 00000000..417b6ce1 --- /dev/null +++ b/packages/container/tools/e2e-tests/src/common/parameters/stringList.ts @@ -0,0 +1,11 @@ +import { defineParameterType } from '@cucumber/cucumber'; + +defineParameterType({ + name: 'stringList', + regexp: /("\w+"(?:(?:, "\w+")* and "\w+")?)/, + transformer: function (stringList: string): string[] { + return stringList + .split(/ and |, /) + .map((item: string): string => item.replaceAll('"', '')); + }, +}); From bd56e2a2ab62b01db7eec898846c566b5990c249 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roberto=20Pintos=20L=C3=B3pez?= Date: Tue, 7 Jan 2025 00:18:09 +0100 Subject: [PATCH 4/7] test(e2e-tests): add bindingScope parameter --- .../e2e-tests/src/binding/parameters/bindingScope.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 packages/container/tools/e2e-tests/src/binding/parameters/bindingScope.ts diff --git a/packages/container/tools/e2e-tests/src/binding/parameters/bindingScope.ts b/packages/container/tools/e2e-tests/src/binding/parameters/bindingScope.ts new file mode 100644 index 00000000..4e83c2dc --- /dev/null +++ b/packages/container/tools/e2e-tests/src/binding/parameters/bindingScope.ts @@ -0,0 +1,10 @@ +import { defineParameterType } from '@cucumber/cucumber'; +import { BindingScope, bindingScopeValues } from '@inversifyjs/core'; + +defineParameterType({ + name: 'bindingScope', + regexp: new RegExp(`"(${Object.values(bindingScopeValues).join('|')})"`), + transformer: function (bindingScope: string): BindingScope { + return bindingScope as BindingScope; + }, +}); From 64f82a2ed8e738843ea9599879bb9d7c10a09318 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roberto=20Pintos=20L=C3=B3pez?= Date: Tue, 7 Jan 2025 00:19:00 +0100 Subject: [PATCH 5/7] refactor(e2e-tests): update BindingParameter with DynamicValueBindingParameter --- .../tools/e2e-tests/src/binding/models/BindingParameter.ts | 5 ++++- .../e2e-tests/src/binding/models/BindingParameterKind.ts | 1 + .../src/binding/models/DynamicValueBindingParameter.ts | 5 +++++ 3 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 packages/container/tools/e2e-tests/src/binding/models/DynamicValueBindingParameter.ts diff --git a/packages/container/tools/e2e-tests/src/binding/models/BindingParameter.ts b/packages/container/tools/e2e-tests/src/binding/models/BindingParameter.ts index ffe50099..5d2552fe 100644 --- a/packages/container/tools/e2e-tests/src/binding/models/BindingParameter.ts +++ b/packages/container/tools/e2e-tests/src/binding/models/BindingParameter.ts @@ -1,3 +1,6 @@ import { ConstantValueBindingParameter } from './ConstantValueBindingParameter'; +import { DynamicValueBindingParameter } from './DynamicValueBindingParameter'; -export type BindingParameter = ConstantValueBindingParameter; +export type BindingParameter = + | ConstantValueBindingParameter + | DynamicValueBindingParameter; diff --git a/packages/container/tools/e2e-tests/src/binding/models/BindingParameterKind.ts b/packages/container/tools/e2e-tests/src/binding/models/BindingParameterKind.ts index 68d53068..4466396c 100644 --- a/packages/container/tools/e2e-tests/src/binding/models/BindingParameterKind.ts +++ b/packages/container/tools/e2e-tests/src/binding/models/BindingParameterKind.ts @@ -1,3 +1,4 @@ export enum BindingParameterKind { constantValue, + dynamicValue, } diff --git a/packages/container/tools/e2e-tests/src/binding/models/DynamicValueBindingParameter.ts b/packages/container/tools/e2e-tests/src/binding/models/DynamicValueBindingParameter.ts new file mode 100644 index 00000000..a739d98d --- /dev/null +++ b/packages/container/tools/e2e-tests/src/binding/models/DynamicValueBindingParameter.ts @@ -0,0 +1,5 @@ +import { BaseBindingParameter } from './BaseBindingParameter'; +import { BindingParameterKind } from './BindingParameterKind'; + +export type DynamicValueBindingParameter = + BaseBindingParameter; From 784d6af8b3bf2c9d5a6f65d9852dcde87c50bfdc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roberto=20Pintos=20L=C3=B3pez?= Date: Tue, 7 Jan 2025 00:19:48 +0100 Subject: [PATCH 6/7] refactor(e2e-tests): update world with ContainerRequests --- .../e2e-tests/src/app/actions/initializeWorld.ts | 3 +++ .../e2e-tests/src/common/models/InversifyWorld.ts | 5 +++++ .../container/actions/setContainerGetRequest.ts | 9 +++++++++ .../calculations/getContainerGetRequestOrFail.ts | 14 ++++++++++++++ 4 files changed, 31 insertions(+) create mode 100644 packages/container/tools/e2e-tests/src/container/actions/setContainerGetRequest.ts create mode 100644 packages/container/tools/e2e-tests/src/container/calculations/getContainerGetRequestOrFail.ts diff --git a/packages/container/tools/e2e-tests/src/app/actions/initializeWorld.ts b/packages/container/tools/e2e-tests/src/app/actions/initializeWorld.ts index a55d0b69..45075876 100644 --- a/packages/container/tools/e2e-tests/src/app/actions/initializeWorld.ts +++ b/packages/container/tools/e2e-tests/src/app/actions/initializeWorld.ts @@ -2,6 +2,9 @@ import { InversifyWorld } from '../../common/models/InversifyWorld'; import { Writable } from '../../common/models/Writable'; export function initializeWorld(this: Writable>): void { + this.containerRequests = { + get: new Map(), + }; this.entities = { bindings: new Map(), containers: new Map(), diff --git a/packages/container/tools/e2e-tests/src/common/models/InversifyWorld.ts b/packages/container/tools/e2e-tests/src/common/models/InversifyWorld.ts index 269a1b64..61e17a88 100644 --- a/packages/container/tools/e2e-tests/src/common/models/InversifyWorld.ts +++ b/packages/container/tools/e2e-tests/src/common/models/InversifyWorld.ts @@ -8,6 +8,11 @@ export interface EntitiesMap { containers: Map; } +export interface ContainerRequests { + get: Map; +} + export interface InversifyWorld extends IWorld { + readonly containerRequests: ContainerRequests; readonly entities: EntitiesMap; } diff --git a/packages/container/tools/e2e-tests/src/container/actions/setContainerGetRequest.ts b/packages/container/tools/e2e-tests/src/container/actions/setContainerGetRequest.ts new file mode 100644 index 00000000..c647323a --- /dev/null +++ b/packages/container/tools/e2e-tests/src/container/actions/setContainerGetRequest.ts @@ -0,0 +1,9 @@ +import { InversifyWorld } from '../../common/models/InversifyWorld'; + +export function setContainerGetRequest( + this: InversifyWorld, + alias: string, + result: unknown, +): void { + this.containerRequests.get.set(alias, result); +} diff --git a/packages/container/tools/e2e-tests/src/container/calculations/getContainerGetRequestOrFail.ts b/packages/container/tools/e2e-tests/src/container/calculations/getContainerGetRequestOrFail.ts new file mode 100644 index 00000000..de871428 --- /dev/null +++ b/packages/container/tools/e2e-tests/src/container/calculations/getContainerGetRequestOrFail.ts @@ -0,0 +1,14 @@ +import { InversifyWorld } from '../../common/models/InversifyWorld'; + +export function getContainerGetRequestOrFail( + this: InversifyWorld, + alias: string, +): unknown { + if (!this.containerRequests.get.has(alias)) { + throw new Error( + `Expected "${alias}" aliased container get request not found`, + ); + } + + return this.containerRequests.get.get(alias); +} From 1a694cc37f4364bfe9d6153cb445321d4727d0ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roberto=20Pintos=20L=C3=B3pez?= Date: Tue, 7 Jan 2025 00:21:21 +0100 Subject: [PATCH 7/7] test(e2e-tests): add binding rule --- .../e2e-tests/features/container/bind.feature | 15 ++++++ .../step-definitions/givenDefinitions.ts | 41 +++++++++++++- .../step-definitions/thenDefinitions.ts | 54 +++++++++++++++++++ .../step-definitions/whenDefinitions.ts | 32 +++++++++++ 4 files changed, 141 insertions(+), 1 deletion(-) create mode 100644 packages/container/tools/e2e-tests/src/container/step-definitions/thenDefinitions.ts create mode 100644 packages/container/tools/e2e-tests/src/container/step-definitions/whenDefinitions.ts diff --git a/packages/container/tools/e2e-tests/features/container/bind.feature b/packages/container/tools/e2e-tests/features/container/bind.feature index 272ee1af..b582d0a9 100644 --- a/packages/container/tools/e2e-tests/features/container/bind.feature +++ b/packages/container/tools/e2e-tests/features/container/bind.feature @@ -11,3 +11,18 @@ Feature: Bind Given a service "service-id" binding to constant value When binding is bound to container Then container acknowledges binding to be bound + + Rule: container binds in the expected scope + + Scenario: A binding is bound to a container and two requests are resolved according to the binding scope + Given a service "service-id" binding to dynamic value in scope + When binding is bound to container + And container gets a "first" value for service "service-id" + And container gets a "second" value for service "service-id" + Then "first" and "second" values are + + Examples: + | binding_scope | equality | + | "Request" | distinct | + | "Singleton" | equal | + | "Transient" | distinct | diff --git a/packages/container/tools/e2e-tests/src/binding/step-definitions/givenDefinitions.ts b/packages/container/tools/e2e-tests/src/binding/step-definitions/givenDefinitions.ts index c9a891ff..2529332b 100644 --- a/packages/container/tools/e2e-tests/src/binding/step-definitions/givenDefinitions.ts +++ b/packages/container/tools/e2e-tests/src/binding/step-definitions/givenDefinitions.ts @@ -1,5 +1,6 @@ import { Given } from '@cucumber/cucumber'; -import { Container } from '@inversifyjs/container'; +import { BindInWhenOnFluentSyntax, Container } from '@inversifyjs/container'; +import { BindingScope, bindingScopeValues } from '@inversifyjs/core'; import { defaultAlias } from '../../common/models/defaultAlias'; import { InversifyWorld } from '../../common/models/InversifyWorld'; @@ -24,9 +25,47 @@ function givenBindingToConstantValue( }); } +function givenBindingToDynamicValue( + this: InversifyWorld, + serviceId: string, + scope: BindingScope, + bindingAlias?: string, +): void { + const parsedBindingAlias: string = bindingAlias ?? defaultAlias; + + setBinding.bind(this)(parsedBindingAlias, { + bind: (container: Container): void => { + const bindSyntax: BindInWhenOnFluentSyntax = container + .bind(serviceId) + .toDynamicValue(() => Symbol()); + + switch (scope) { + case bindingScopeValues.Request: + bindSyntax.inRequestScope(); + break; + case bindingScopeValues.Singleton: + bindSyntax.inSingletonScope(); + break; + case bindingScopeValues.Transient: + bindSyntax.inTransientScope(); + break; + } + }, + kind: BindingParameterKind.dynamicValue, + serviceIdentifier: serviceId, + }); +} + Given( 'a service {string} binding to constant value', function (serviceId: string): void { givenBindingToConstantValue.bind(this)(serviceId); }, ); + +Given( + 'a service {string} binding to dynamic value in {bindingScope} scope', + function (serviceId: string, scope: BindingScope): void { + givenBindingToDynamicValue.bind(this)(serviceId, scope); + }, +); diff --git a/packages/container/tools/e2e-tests/src/container/step-definitions/thenDefinitions.ts b/packages/container/tools/e2e-tests/src/container/step-definitions/thenDefinitions.ts new file mode 100644 index 00000000..d8942fa9 --- /dev/null +++ b/packages/container/tools/e2e-tests/src/container/step-definitions/thenDefinitions.ts @@ -0,0 +1,54 @@ +import assert from 'node:assert/strict'; + +import { Then } from '@cucumber/cucumber'; + +import { InversifyWorld } from '../../common/models/InversifyWorld'; +import { getContainerGetRequestOrFail } from '../calculations/getContainerGetRequestOrFail'; + +function getValues(this: InversifyWorld, valueAliases: string[]): unknown[] { + return valueAliases.map((valueAlias: string): unknown => + getContainerGetRequestOrFail.bind(this)(valueAlias), + ); +} + +function thenValuesAreDistinct( + this: InversifyWorld, + valueAliases: string[], +): void { + assert.ok( + getValues + .bind(this)(valueAliases) + .every( + (value: unknown, index: number, array: unknown[]): boolean => + array.at(index - 1) !== value, + ), + ); +} + +function thenValuesAreEqual( + this: InversifyWorld, + valueAliases: string[], +): void { + assert.ok( + getValues + .bind(this)(valueAliases) + .every( + (value: unknown, index: number, array: unknown[]): boolean => + array.at(index - 1) === value, + ), + ); +} + +Then( + '{stringList} values are distinct', + function (valueAliases: string[]): void { + thenValuesAreDistinct.bind(this)(valueAliases); + }, +); + +Then( + '{stringList} values are equal', + function (valueAliases: string[]): void { + thenValuesAreEqual.bind(this)(valueAliases); + }, +); diff --git a/packages/container/tools/e2e-tests/src/container/step-definitions/whenDefinitions.ts b/packages/container/tools/e2e-tests/src/container/step-definitions/whenDefinitions.ts new file mode 100644 index 00000000..efa5b797 --- /dev/null +++ b/packages/container/tools/e2e-tests/src/container/step-definitions/whenDefinitions.ts @@ -0,0 +1,32 @@ +import { When } from '@cucumber/cucumber'; + +import { defaultAlias } from '../../common/models/defaultAlias'; +import { InversifyWorld } from '../../common/models/InversifyWorld'; +import { setContainerGetRequest } from '../actions/setContainerGetRequest'; +import { getContainerOrFail } from '../calculations/getContainerOrFail'; + +function whenContainerGetsValueForService( + this: InversifyWorld, + serviceId: string, + containerAlias?: string, + valueAlias?: string, +): void { + const parsedContainerAlias: string = containerAlias ?? defaultAlias; + const parsedValueAlias: string = valueAlias ?? defaultAlias; + + setContainerGetRequest.bind(this)( + parsedValueAlias, + getContainerOrFail.bind(this)(parsedContainerAlias).get(serviceId), + ); +} + +When( + 'container gets a {string} value for service {string}', + function (valueAlias: string, serviceId: string): void { + whenContainerGetsValueForService.bind(this)( + serviceId, + undefined, + valueAlias, + ); + }, +);