@entifix/testing-e2e 0.4.1 → 0.5.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/fixtures/entity-backend.d.ts.map +1 -1
- package/dist/fixtures/entity-backend.js +3 -5
- package/dist/fixtures/entity-backend.js.map +1 -1
- package/dist/playwright/assert-expected-server.d.ts +1 -1
- package/dist/playwright/assert-expected-server.d.ts.map +1 -1
- package/dist/playwright/assert-expected-server.js +1 -1
- package/dist/playwright/assert-expected-server.js.map +1 -1
- package/package.json +10 -10
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entity-backend.d.ts","sourceRoot":"","sources":["../../src/fixtures/entity-backend.ts"],"names":[],"mappings":"AAOA,OAAO,EAEL,KAAK,MAAM,EACX,KAAK,iBAAiB,EACtB,KAAK,QAAQ,EAMd,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"entity-backend.d.ts","sourceRoot":"","sources":["../../src/fixtures/entity-backend.ts"],"names":[],"mappings":"AAOA,OAAO,EAEL,KAAK,MAAM,EACX,KAAK,iBAAiB,EACtB,KAAK,QAAQ,EAMd,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,KAAK,WAAW,EAEjB,MAAM,+BAA+B,CAAC;AAGvC;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;CACxB;AAED,8EAA8E;AAC9E,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEjD,MAAM,WAAW,oBAAoB;IACnC,mDAAmD;IACnD,IAAI,CAAC,EAAE,aAAa,CAAC,UAAU,CAAC,CAAC;CAClC;AAED,MAAM,WAAW,aAAa;IAC5B,sEAAsE;IACtE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,0EAA0E;IAC1E,IAAI,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;IACxD,4BAA4B;IAC5B,GAAG,CAAC,EAAE,EAAE,QAAQ,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;IAC5C,2EAA2E;IAC3E,IAAI,CAAC,IAAI,EAAE,aAAa,CAAC,UAAU,CAAC,GAAG,IAAI,CAAC;IAC5C,wEAAwE;IACxE,IAAI,IAAI,UAAU,EAAE,CAAC;IACrB,yDAAyD;IACzD,QAAQ,CAAC,EAAE,EAAE,WAAW,CAAC;CAC1B;AAcD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,iBAAiB,GAAI,OAAO,SAAS,MAAM,EACtD,mBAAmB,iBAAiB,CAAC,OAAO,CAAC,EAC7C,WAAe,oBAAyB,KACvC,aA8FF,CAAC"}
|
|
@@ -2,7 +2,6 @@ import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
|
2
2
|
import { EntityIdTag, EntityLoadRequestTag, EntityRepositoryTag, getUCFactory, loadUCFactory } from '@entifix/business';
|
|
3
3
|
import { envelopeEntityName, makeEntityEnvelope, makeEntityPageEnvelope, parseLoadRequestParams } from '@entifix/core';
|
|
4
4
|
import { makeMongoRepository } from '@entifix/mongo';
|
|
5
|
-
import { stubConfigurationLayer } from '@entifix/testing-unit';
|
|
6
5
|
import { makeFakeMongoDb } from '@entifix/testing-unit/drivers';
|
|
7
6
|
import { Effect } from 'effect';
|
|
8
7
|
const collectionLinks = (key)=>[
|
|
@@ -67,8 +66,7 @@ const entityLinks = (key, id)=>[
|
|
|
67
66
|
const repository = makeMongoRepository(// The driver fake is typed `unknown` so `testing-unit` need not depend on
|
|
68
67
|
// the mongodb types; the cast is the documented way to hand it over.
|
|
69
68
|
db.db, entityConstructor);
|
|
70
|
-
|
|
71
|
-
const provided = (effect)=>Effect.provide(effect, stubConfigurationLayer());
|
|
69
|
+
const run = (effect)=>Effect.runPromise(effect);
|
|
72
70
|
return {
|
|
73
71
|
key,
|
|
74
72
|
list: async (search)=>{
|
|
@@ -90,7 +88,7 @@ const entityLinks = (key, id)=>[
|
|
|
90
88
|
};
|
|
91
89
|
}
|
|
92
90
|
try {
|
|
93
|
-
const page = await run(
|
|
91
|
+
const page = await run(loadUCFactory().pipe(Effect.provideService(EntityRepositoryTag, repository), Effect.provideService(EntityLoadRequestTag, request)));
|
|
94
92
|
return {
|
|
95
93
|
status: 200,
|
|
96
94
|
body: makeEntityPageEnvelope(entityConstructor, page, collectionLinks(key))
|
|
@@ -108,7 +106,7 @@ const entityLinks = (key, id)=>[
|
|
|
108
106
|
}
|
|
109
107
|
},
|
|
110
108
|
get: async (id)=>{
|
|
111
|
-
const entity = await run(
|
|
109
|
+
const entity = await run(getUCFactory().pipe(Effect.provideService(EntityRepositoryTag, repository), Effect.provideService(EntityIdTag, id), Effect.option));
|
|
112
110
|
return entity._tag === 'None' ? {
|
|
113
111
|
status: 404,
|
|
114
112
|
body: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/fixtures/entity-backend.ts"],"sourcesContent":["import {\n EntityIdTag,\n EntityLoadRequestTag,\n EntityRepositoryTag,\n getUCFactory,\n loadUCFactory,\n} from '@entifix/business';\nimport {\n type EntifixEnvelopeLink,\n type Entity,\n type EntityConstructor,\n type EntityId,\n type EntityLoadRequest,\n envelopeEntityName,\n makeEntityEnvelope,\n makeEntityPageEnvelope,\n parseLoadRequestParams,\n} from '@entifix/core';\nimport { makeMongoRepository } from '@entifix/mongo';\nimport {
|
|
1
|
+
{"version":3,"sources":["../../src/fixtures/entity-backend.ts"],"sourcesContent":["import {\n EntityIdTag,\n EntityLoadRequestTag,\n EntityRepositoryTag,\n getUCFactory,\n loadUCFactory,\n} from '@entifix/business';\nimport {\n type EntifixEnvelopeLink,\n type Entity,\n type EntityConstructor,\n type EntityId,\n type EntityLoadRequest,\n envelopeEntityName,\n makeEntityEnvelope,\n makeEntityPageEnvelope,\n parseLoadRequestParams,\n} from '@entifix/core';\nimport { makeMongoRepository } from '@entifix/mongo';\nimport {\n type FakeMongoDb,\n makeFakeMongoDb,\n} from '@entifix/testing-unit/drivers';\nimport { Effect } from 'effect';\n\n/**\n * A response as an HTTP transport would send it. `body` is an entifix envelope\n * or an error record; it stays `unknown` here so the backend is not coupled to\n * one transport's body type.\n */\nexport interface BackendResponse {\n readonly status: number;\n readonly body: unknown;\n}\n\n/** Rows as they are stored — the entity wire shape, exactly like the seed. */\nexport type BackendRow = Record<string, unknown>;\n\nexport interface EntityBackendOptions {\n /** Initial documents, in the entity wire shape. */\n seed?: ReadonlyArray<BackendRow>;\n}\n\nexport interface EntityBackend {\n /** The entity `key` — collection name and REST path segment alike. */\n readonly key: string;\n /** `GET /api/<key>?<search>` — filtering, sorting and paging included. */\n list(search: URLSearchParams): Promise<BackendResponse>;\n /** `GET /api/<key>/:id`. */\n get(id: EntityId): Promise<BackendResponse>;\n /** Replaces the stored rows, so one fixture can serve several journeys. */\n seed(rows: ReadonlyArray<BackendRow>): void;\n /** The rows currently stored, for assertions about what a write did. */\n rows(): BackendRow[];\n /** The underlying driver fake, for failure injection. */\n readonly db: FakeMongoDb;\n}\n\nconst collectionLinks = (key: string): EntifixEnvelopeLink[] => [\n { rel: 'self', href: `/api/${key}`, method: 'GET' },\n { rel: 'create', href: `/api/${key}`, method: 'POST' },\n];\n\nconst entityLinks = (key: string, id: EntityId): EntifixEnvelopeLink[] => [\n { rel: 'self', href: `/api/${key}/${String(id)}`, method: 'GET' },\n { rel: 'list', href: `/api/${key}`, method: 'GET' },\n { rel: 'update', href: `/api/${key}/${String(id)}`, method: 'PUT' },\n { rel: 'delete', href: `/api/${key}/${String(id)}`, method: 'DELETE' },\n];\n\n/**\n * A read backend for one entity, assembled from the PRODUCTION query pipeline:\n *\n * ```\n * parseLoadRequestParams → loadUCFactory → makeMongoRepository → fake driver\n * (core: RSQL + the (business: (mongo-client: (testing-unit)\n * filterable/sortable the use-case) filter translation)\n * allowlist)\n * ```\n *\n * Nothing here re-implements query semantics. That is the whole point: a mock\n * backend that answers a filter with \"all the rows\" makes a green `mock` run\n * meaningless, and a mock backend that implements its *own* RSQL makes the two\n * profiles disagree the first time an operator is added. Because the real\n * pipeline runs, `mock` and `live` agree on filtering, sorting, paging, and on\n * the `400 invalid query` the metadata allowlist produces.\n *\n * Writes are deliberately absent: a `POST` in the admin service is a\n * *transaction* answering `202`, and a fixture that answered `201` instead\n * would be a lie about the protocol rather than a stand-in for it.\n */\nexport const makeEntityBackend = <TEntity extends Entity>(\n entityConstructor: EntityConstructor<TEntity>,\n { seed = [] }: EntityBackendOptions = {},\n): EntityBackend => {\n const key = envelopeEntityName(entityConstructor);\n const db = makeFakeMongoDb({ [key]: seed.map(row => ({ ...row })) });\n const repository = makeMongoRepository(\n // The driver fake is typed `unknown` so `testing-unit` need not depend on\n // the mongodb types; the cast is the documented way to hand it over.\n db.db as Parameters<typeof makeMongoRepository>[0],\n entityConstructor,\n );\n\n const run = <TValue, TError>(\n effect: Effect.Effect<TValue, TError, never>,\n ): Promise<TValue> => Effect.runPromise(effect);\n\n return {\n key,\n\n list: async search => {\n let request: EntityLoadRequest;\n try {\n request = parseLoadRequestParams(\n entityConstructor,\n search,\n ) as unknown as EntityLoadRequest;\n } catch (error) {\n // The same 400 the service answers: an unparseable expression, or a\n // member the entity never declared filterable/sortable. The codec only\n // ever throws `EntifixBuildError` here, so there is no second case to\n // discriminate — anything else would be a bug in the codec, and\n // reporting it verbatim is more useful than hiding it behind a 500.\n return {\n status: 400,\n body: { error: 'invalid query', detail: String(error) },\n };\n }\n\n try {\n const page = await run(\n loadUCFactory<TEntity>().pipe(\n Effect.provideService(EntityRepositoryTag, repository),\n Effect.provideService(EntityLoadRequestTag, request),\n ),\n );\n\n return {\n status: 200,\n body: makeEntityPageEnvelope(\n entityConstructor,\n page,\n collectionLinks(key),\n ),\n };\n } catch (error) {\n // A repository that fails is a 500, exactly as in the service: the\n // request was valid, the store was not reachable.\n return {\n status: 500,\n body: { error: 'request failed', detail: String(error) },\n };\n }\n },\n\n get: async id => {\n const entity = await run(\n getUCFactory<TEntity>().pipe(\n Effect.provideService(EntityRepositoryTag, repository),\n Effect.provideService(EntityIdTag, id),\n Effect.option,\n ),\n );\n\n return entity._tag === 'None'\n ? { status: 404, body: { message: `${key} not found` } }\n : {\n status: 200,\n body: makeEntityEnvelope(\n entityConstructor,\n entity.value,\n entityLinks(key, id),\n ),\n };\n },\n\n seed: rows => {\n db.seed(\n key,\n rows.map(row => ({ ...row })),\n );\n },\n\n rows: () => db.read(key),\n\n db,\n };\n};\n"],"names":["EntityIdTag","EntityLoadRequestTag","EntityRepositoryTag","getUCFactory","loadUCFactory","envelopeEntityName","makeEntityEnvelope","makeEntityPageEnvelope","parseLoadRequestParams","makeMongoRepository","makeFakeMongoDb","Effect","collectionLinks","key","rel","href","method","entityLinks","id","String","makeEntityBackend","entityConstructor","seed","db","map","row","repository","run","effect","runPromise","list","search","request","error","status","body","detail","page","pipe","provideService","get","entity","option","_tag","message","value","rows","read"],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type E2eEnv } from '../profile/profile.js';
|
|
2
2
|
/** Opts out of the guard, for a developer who knows what they are attaching to. */
|
|
3
|
-
export declare const ALLOW_DEV_SERVER_VAR = "
|
|
3
|
+
export declare const ALLOW_DEV_SERVER_VAR = "ENTIFIX_E2E_ALLOW_DEV_SERVER";
|
|
4
4
|
export interface AssertExpectedServerOptions {
|
|
5
5
|
/** Where the suite is pointed, e.g. `http://localhost:3001`. */
|
|
6
6
|
readonly baseURL: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"assert-expected-server.d.ts","sourceRoot":"","sources":["../../src/playwright/assert-expected-server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,MAAM,EAAiB,MAAM,uBAAuB,CAAC;AAEnE,mFAAmF;AACnF,eAAO,MAAM,oBAAoB,
|
|
1
|
+
{"version":3,"file":"assert-expected-server.d.ts","sourceRoot":"","sources":["../../src/playwright/assert-expected-server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,MAAM,EAAiB,MAAM,uBAAuB,CAAC;AAEnE,mFAAmF;AACnF,eAAO,MAAM,oBAAoB,iCAAiC,CAAC;AA0BnE,MAAM,WAAW,2BAA2B;IAC1C,gEAAgE;IAChE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IAClC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CAC3C;AAiBD;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,oBAAoB,GAAU,oCAKxC,2BAA2B,KAAG,OAAO,CAAC,IAAI,CAiC5C,CAAC;AAKF;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,wBAAwB,GACnC,SAAS,MAAM,GAAG,SAAS,KAC1B,OAAO,CAAC,IAAI,CAKd,CAAC"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { isMockProfile } from '../profile/profile.js';
|
|
2
|
-
/** Opts out of the guard, for a developer who knows what they are attaching to. */ export const ALLOW_DEV_SERVER_VAR = '
|
|
2
|
+
/** Opts out of the guard, for a developer who knows what they are attaching to. */ export const ALLOW_DEV_SERVER_VAR = 'ENTIFIX_E2E_ALLOW_DEV_SERVER';
|
|
3
3
|
/**
|
|
4
4
|
* Liveness, not the config's `readyPath`: every app answers it, it answers from
|
|
5
5
|
* the process alone, and it carries the one field this check needs. `readyPath`
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/playwright/assert-expected-server.ts"],"sourcesContent":["import { type E2eEnv, isMockProfile } from '../profile/profile.js';\n\n/** Opts out of the guard, for a developer who knows what they are attaching to. */\nexport const ALLOW_DEV_SERVER_VAR = '
|
|
1
|
+
{"version":3,"sources":["../../src/playwright/assert-expected-server.ts"],"sourcesContent":["import { type E2eEnv, isMockProfile } from '../profile/profile.js';\n\n/** Opts out of the guard, for a developer who knows what they are attaching to. */\nexport const ALLOW_DEV_SERVER_VAR = 'ENTIFIX_E2E_ALLOW_DEV_SERVER';\n\n/**\n * Liveness, not the config's `readyPath`: every app answers it, it answers from\n * the process alone, and it carries the one field this check needs. `readyPath`\n * differs per app and defaults to `/`, which returns HTML.\n */\nconst LIVENESS_PATH = '/api/health/live';\n\n/**\n * The second signal, which needs no cooperation from the app at all.\n *\n * `next dev` serves its static assets under the literal segment `development`;\n * a production build uses a hashed build id, so this 404s under `next start`.\n * It is a Next internal and therefore the *fallback*, but it is the one that\n * survives the case that motivates it: an `nx e2e` run builds into the same\n * `.next` the running dev server owns, which corrupts that server's Turbopack\n * cache and makes every app route — liveness included — answer `500`. A guard\n * that only asked the app would degrade to \"cannot tell\" at exactly the moment\n * it is needed.\n */\nconst DEV_ASSET_PATH = '/_next/static/development/_buildManifest.js';\n\n/** A probe that hangs must not hold the whole run hostage. */\nconst PROBE_TIMEOUT_MS = 5_000;\n\nexport interface AssertExpectedServerOptions {\n /** Where the suite is pointed, e.g. `http://localhost:3001`. */\n readonly baseURL: string;\n readonly env?: E2eEnv;\n readonly fetchImpl?: typeof fetch;\n readonly warn?: (message: string) => void;\n}\n\nconst describeMismatch = (baseURL: string): string =>\n [\n `The hermetic e2e profile attached to a DEVELOPMENT server at ${baseURL}.`,\n '',\n 'Playwright reuses whatever already listens on the app port, so this run would',\n 'test `next dev` — a different bundle, no prerender, and with the dev fleet',\n \"behind it the real services instead of this profile's fakes. It can fail, or\",\n 'pass, for reasons that have nothing to do with the code under test. The build',\n 'this run just made also writes the same `.next` the dev server owns, which',\n 'corrupts its Turbopack cache on the way past.',\n '',\n 'Stop the dev server on that port and run again, or set',\n `${ALLOW_DEV_SERVER_VAR}=1 to accept the risk.`,\n ].join('\\n');\n\n/**\n * Refuses to run the hermetic suite against a development server.\n *\n * `reuseExistingServer` is what makes a local run fast, and turning it off costs\n * a production build every time. So the reuse stays and the *assumption* is\n * checked instead: the `mock` profile's whole claim is that it is hermetic, and\n * attached to a dev server it is not. Turning a silent wrong answer into a loud\n * failure is the trade — see issue #69.\n *\n * `live` is exempt on purpose: it expects an already-running app and makes no\n * hermeticity claim, so which build answers is the operator's business.\n *\n * Only when **both** signals are unreadable does the run continue, with a\n * warning: an app that exposes neither is not a reason to refuse to test it.\n */\nexport const assertExpectedServer = async ({\n baseURL,\n env = process.env,\n fetchImpl = fetch,\n warn = message => console.warn(message),\n}: AssertExpectedServerOptions): Promise<void> => {\n if (!isMockProfile(env)) return;\n\n const override = env[ALLOW_DEV_SERVER_VAR];\n if (override !== undefined && override !== '') return;\n\n const get = async (path: string): Promise<Response | undefined> => {\n try {\n return await fetchImpl(`${baseURL}${path}`, {\n signal: AbortSignal.timeout(PROBE_TIMEOUT_MS),\n });\n } catch {\n return undefined;\n }\n };\n\n const liveness = await get(LIVENESS_PATH);\n if (liveness?.ok === true) {\n const mode = ((await liveness.json()) as { mode?: unknown }).mode;\n if (mode === 'production') return;\n if (mode === 'development') throw new Error(describeMismatch(baseURL));\n }\n\n // The app could not answer for itself. Ask Next directly.\n const devAsset = await get(DEV_ASSET_PATH);\n if (devAsset === undefined) {\n warn(\n `e2e: could not reach ${baseURL}; cannot tell which build is serving.`,\n );\n return;\n }\n\n if (devAsset.ok) throw new Error(describeMismatch(baseURL));\n};\n\n/** One probe per worker process, whatever the outcome. */\nlet verdict: Promise<void> | undefined;\n\n/**\n * The form `defineEntifixE2eTest` calls, from an auto fixture.\n *\n * A fixture rather than `globalSetup`, which is where this naturally belongs:\n * Playwright loads a `globalSetup` file through a different module path than\n * the config and the specs, and this package is `\"type\": \"module\"` while an\n * app's `playwright.config.ts` is CJS — the same file transpiles for one and\n * throws `exports is not defined in ES module scope` for the other. The\n * fixture rides the path that already works, at the cost of reporting the\n * refusal once per test rather than once per run.\n *\n * The promise is cached, so the probe happens once and every subsequent test\n * rejects with the same error instead of re-asking a server that will not have\n * changed mid-run.\n */\nexport const assertExpectedServerOnce = (\n baseURL: string | undefined,\n): Promise<void> => {\n if (baseURL === undefined || baseURL === '') return Promise.resolve();\n\n verdict ??= assertExpectedServer({ baseURL });\n return verdict;\n};\n"],"names":["isMockProfile","ALLOW_DEV_SERVER_VAR","LIVENESS_PATH","DEV_ASSET_PATH","PROBE_TIMEOUT_MS","describeMismatch","baseURL","join","assertExpectedServer","env","process","fetchImpl","fetch","warn","message","console","override","undefined","get","path","signal","AbortSignal","timeout","liveness","ok","mode","json","Error","devAsset","verdict","assertExpectedServerOnce","Promise","resolve"],"mappings":"AAAA,SAAsBA,aAAa,QAAQ,wBAAwB;AAEnE,iFAAiF,GACjF,OAAO,MAAMC,uBAAuB,+BAA+B;AAEnE;;;;CAIC,GACD,MAAMC,gBAAgB;AAEtB;;;;;;;;;;;CAWC,GACD,MAAMC,iBAAiB;AAEvB,4DAA4D,GAC5D,MAAMC,mBAAmB;AAUzB,MAAMC,mBAAmB,CAACC,UACxB;QACE,CAAC,6DAA6D,EAAEA,QAAQ,CAAC,CAAC;QAC1E;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA,GAAGL,qBAAqB,sBAAsB,CAAC;KAChD,CAACM,IAAI,CAAC;AAET;;;;;;;;;;;;;;CAcC,GACD,OAAO,MAAMC,uBAAuB,OAAO,EACzCF,OAAO,EACPG,MAAMC,QAAQD,GAAG,EACjBE,YAAYC,KAAK,EACjBC,OAAOC,CAAAA,UAAWC,QAAQF,IAAI,CAACC,QAAQ,EACX;IAC5B,IAAI,CAACd,cAAcS,MAAM;IAEzB,MAAMO,WAAWP,GAAG,CAACR,qBAAqB;IAC1C,IAAIe,aAAaC,aAAaD,aAAa,IAAI;IAE/C,MAAME,MAAM,OAAOC;QACjB,IAAI;YACF,OAAO,MAAMR,UAAU,GAAGL,UAAUa,MAAM,EAAE;gBAC1CC,QAAQC,YAAYC,OAAO,CAAClB;YAC9B;QACF,EAAE,eAAM;YACN,OAAOa;QACT;IACF;IAEA,MAAMM,WAAW,MAAML,IAAIhB;IAC3B,IAAIqB,CAAAA,4BAAAA,SAAUC,EAAE,MAAK,MAAM;QACzB,MAAMC,OAAO,AAAE,CAAA,MAAMF,SAASG,IAAI,EAAC,EAA0BD,IAAI;QACjE,IAAIA,SAAS,cAAc;QAC3B,IAAIA,SAAS,eAAe,MAAM,IAAIE,MAAMtB,iBAAiBC;IAC/D;IAEA,0DAA0D;IAC1D,MAAMsB,WAAW,MAAMV,IAAIf;IAC3B,IAAIyB,aAAaX,WAAW;QAC1BJ,KACE,CAAC,qBAAqB,EAAEP,QAAQ,qCAAqC,CAAC;QAExE;IACF;IAEA,IAAIsB,SAASJ,EAAE,EAAE,MAAM,IAAIG,MAAMtB,iBAAiBC;AACpD,EAAE;AAEF,wDAAwD,GACxD,IAAIuB;AAEJ;;;;;;;;;;;;;;CAcC,GACD,OAAO,MAAMC,2BAA2B,CACtCxB;IAEA,IAAIA,YAAYW,aAAaX,YAAY,IAAI,OAAOyB,QAAQC,OAAO;IAEnEH,kBAAAA,UAAAA,UAAYrB,qBAAqB;QAAEF;IAAQ;IAC3C,OAAOuB;AACT,EAAE"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@entifix/testing-e2e",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "End-to-end testing with one set of journeys and two profiles: mocked backends by default, real infrastructure on demand.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -67,9 +67,9 @@
|
|
|
67
67
|
},
|
|
68
68
|
"dependencies": {
|
|
69
69
|
"@swc/helpers": "0.5.23",
|
|
70
|
-
"@entifix/
|
|
71
|
-
"@entifix/
|
|
72
|
-
"@entifix/
|
|
70
|
+
"@entifix/core": "0.5.0",
|
|
71
|
+
"@entifix/testing-unit": "0.5.0",
|
|
72
|
+
"@entifix/business": "0.5.0"
|
|
73
73
|
},
|
|
74
74
|
"peerDependencies": {
|
|
75
75
|
"@nx/devkit": ">=23 <24",
|
|
@@ -80,9 +80,9 @@
|
|
|
80
80
|
"msw": ">=3",
|
|
81
81
|
"@msw/playwright": ">=0.7",
|
|
82
82
|
"vitest": "*",
|
|
83
|
-
"@entifix/amqp": "0.
|
|
84
|
-
"@entifix/
|
|
85
|
-
"@entifix/
|
|
83
|
+
"@entifix/amqp": "0.5.0",
|
|
84
|
+
"@entifix/redis": "0.5.0",
|
|
85
|
+
"@entifix/mongo": "0.5.0"
|
|
86
86
|
},
|
|
87
87
|
"peerDependenciesMeta": {
|
|
88
88
|
"@msw/playwright": {
|
|
@@ -120,8 +120,8 @@
|
|
|
120
120
|
"@nx/devkit": "23.3.0",
|
|
121
121
|
"@nx/playwright": "23.3.0",
|
|
122
122
|
"effect": "4.0.5",
|
|
123
|
-
"@entifix/amqp": "0.
|
|
124
|
-
"@entifix/mongo": "0.
|
|
125
|
-
"@entifix/redis": "0.
|
|
123
|
+
"@entifix/amqp": "0.5.0",
|
|
124
|
+
"@entifix/mongo": "0.5.0",
|
|
125
|
+
"@entifix/redis": "0.5.0"
|
|
126
126
|
}
|
|
127
127
|
}
|