@entifix/testing-e2e 0.1.0-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.
Files changed (63) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +150 -0
  3. package/dist/fixtures/entity-backend.d.ts +54 -0
  4. package/dist/fixtures/entity-backend.d.ts.map +1 -0
  5. package/dist/fixtures/entity-backend.js +130 -0
  6. package/dist/fixtures/entity-backend.js.map +1 -0
  7. package/dist/fixtures/entity-handlers.d.ts +32 -0
  8. package/dist/fixtures/entity-handlers.d.ts.map +1 -0
  9. package/dist/fixtures/entity-handlers.js +39 -0
  10. package/dist/fixtures/entity-handlers.js.map +1 -0
  11. package/dist/fixtures/fake-infrastructure.d.ts +61 -0
  12. package/dist/fixtures/fake-infrastructure.d.ts.map +1 -0
  13. package/dist/fixtures/fake-infrastructure.js +77 -0
  14. package/dist/fixtures/fake-infrastructure.js.map +1 -0
  15. package/dist/fixtures/index.d.ts +5 -0
  16. package/dist/fixtures/index.d.ts.map +1 -0
  17. package/dist/fixtures/index.js +6 -0
  18. package/dist/fixtures/index.js.map +1 -0
  19. package/dist/fixtures/signing-key.d.ts +27 -0
  20. package/dist/fixtures/signing-key.d.ts.map +1 -0
  21. package/dist/fixtures/signing-key.js +91 -0
  22. package/dist/fixtures/signing-key.js.map +1 -0
  23. package/dist/index.d.ts +2 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.js +3 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/playwright/assert-expected-server.d.ts +43 -0
  28. package/dist/playwright/assert-expected-server.d.ts.map +1 -0
  29. package/dist/playwright/assert-expected-server.js +98 -0
  30. package/dist/playwright/assert-expected-server.js.map +1 -0
  31. package/dist/playwright/config.d.ts +78 -0
  32. package/dist/playwright/config.d.ts.map +1 -0
  33. package/dist/playwright/config.js +96 -0
  34. package/dist/playwright/config.js.map +1 -0
  35. package/dist/playwright/entity-table.d.ts +69 -0
  36. package/dist/playwright/entity-table.d.ts.map +1 -0
  37. package/dist/playwright/entity-table.js +129 -0
  38. package/dist/playwright/entity-table.js.map +1 -0
  39. package/dist/playwright/index.d.ts +6 -0
  40. package/dist/playwright/index.d.ts.map +1 -0
  41. package/dist/playwright/index.js +7 -0
  42. package/dist/playwright/index.js.map +1 -0
  43. package/dist/playwright/session.d.ts +85 -0
  44. package/dist/playwright/session.d.ts.map +1 -0
  45. package/dist/playwright/session.js +250 -0
  46. package/dist/playwright/session.js.map +1 -0
  47. package/dist/playwright/test.d.ts +51 -0
  48. package/dist/playwright/test.d.ts.map +1 -0
  49. package/dist/playwright/test.js +79 -0
  50. package/dist/playwright/test.js.map +1 -0
  51. package/dist/profile/profile.d.ts +43 -0
  52. package/dist/profile/profile.d.ts.map +1 -0
  53. package/dist/profile/profile.js +51 -0
  54. package/dist/profile/profile.js.map +1 -0
  55. package/dist/service/define-service-e2e.d.ts +54 -0
  56. package/dist/service/define-service-e2e.d.ts.map +1 -0
  57. package/dist/service/define-service-e2e.js +57 -0
  58. package/dist/service/define-service-e2e.js.map +1 -0
  59. package/dist/service/index.d.ts +2 -0
  60. package/dist/service/index.d.ts.map +1 -0
  61. package/dist/service/index.js +3 -0
  62. package/dist/service/index.js.map +1 -0
  63. package/package.json +131 -0
@@ -0,0 +1,79 @@
1
+ import { defineNetworkFixture } from '@msw/playwright';
2
+ import { test as base } from '@playwright/test';
3
+ import { isMockProfile } from '../profile/profile.js';
4
+ import { assertExpectedServerOnce } from './assert-expected-server.js';
5
+ /**
6
+ * In `live`, the network is untouched — so reaching for it is a mistake worth
7
+ * reporting rather than a silent no-op that makes a spec assert nothing.
8
+ */ const liveNetwork = ()=>new Proxy({}, {
9
+ get (_target, property) {
10
+ throw new Error(`The network fixture is not available in the live profile (tried to use "${String(property)}"). Move this expectation into a *.mock.spec.ts file.`);
11
+ }
12
+ });
13
+ /**
14
+ * Playwright's own `test`, plus the reused-server guard and nothing else.
15
+ *
16
+ * For a spec that wants no msw network and no seeded session — proving the
17
+ * unauthenticated redirect, say. It exists so that "this spec needs no
18
+ * fixtures" cannot quietly mean "this spec is not checked against the artifact
19
+ * it is running on": importing `test` straight from `@playwright/test` is what
20
+ * left the account journeys resolving a principal against a live auth-service
21
+ * while claiming to be hermetic.
22
+ */ export const baseTest = base.extend({
23
+ expectedServer: [
24
+ async ({ baseURL }, use)=>{
25
+ await assertExpectedServerOnce(baseURL);
26
+ await use();
27
+ },
28
+ {
29
+ auto: true
30
+ }
31
+ ]
32
+ });
33
+ /**
34
+ * Builds the `test` an entifix app e2e project imports.
35
+ *
36
+ * In `mock` it installs the msw handlers over Playwright's `page.route()`
37
+ * (via `@msw/playwright`), so the browser talks to the fixture backend and the
38
+ * app itself is untouched — no worker script, no `NEXT_PUBLIC_*` switch, no
39
+ * mocking code shipped in the application bundle. In `live` the fixture stands
40
+ * down and the same specs hit the real service.
41
+ */ export const defineEntifixE2eTest = ({ handlers = [], passthroughOrigins = [] } = {})=>baseTest.extend({
42
+ handlers: [
43
+ handlers,
44
+ {
45
+ option: true
46
+ }
47
+ ],
48
+ network: [
49
+ async ({ context, handlers: activeHandlers }, use)=>{
50
+ if (!isMockProfile()) {
51
+ await use(liveNetwork());
52
+ return;
53
+ }
54
+ const network = defineNetworkFixture({
55
+ context,
56
+ handlers: activeHandlers,
57
+ // An unstubbed *service* request must fail the test rather than
58
+ // escape to the network: a spec that passes because it silently
59
+ // talked to nothing is worse than one that fails. The app's own
60
+ // origin is exempt — it legitimately serves itself.
61
+ onUnhandledRequest: (request, print)=>{
62
+ if (passthroughOrigins.some((origin)=>request.url.startsWith(origin))) {
63
+ return;
64
+ }
65
+ print.error();
66
+ }
67
+ });
68
+ await network.enable();
69
+ await use(network);
70
+ await network.disable();
71
+ },
72
+ {
73
+ auto: true
74
+ }
75
+ ]
76
+ });
77
+ export { expect } from '@playwright/test';
78
+
79
+ //# sourceMappingURL=test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/playwright/test.ts"],"sourcesContent":["import { defineNetworkFixture, type NetworkFixture } from '@msw/playwright';\nimport { test as base } from '@playwright/test';\nimport type { AnyHandler } from 'msw';\n\nimport { isMockProfile } from '../profile/profile.js';\nimport { assertExpectedServerOnce } from './assert-expected-server.js';\n\nexport interface EntifixE2eFixtures {\n /** The handlers the mock profile starts from. Overridable per project. */\n handlers: AnyHandler[];\n /** The msw network, for per-test overrides in a `*.mock.spec.ts`. */\n network: NetworkFixture;\n /** Refuses a hermetic run against a reused development server. */\n expectedServer: void;\n}\n\nexport interface EntifixE2eTestOptions {\n /**\n * The mock backend. Ignored in the `live` profile, where the app talks to a\n * real service.\n */\n handlers?: AnyHandler[];\n /**\n * Origins whose unstubbed requests are allowed through — in practice the app\n * under test itself, which serves its own documents, RSC payloads and\n * dev-tooling endpoints.\n *\n * Everything else must be stubbed: a request to a *service* that nobody\n * handled fails the test rather than escaping to a machine that may not even\n * be running.\n */\n passthroughOrigins?: string[];\n}\n\n/**\n * In `live`, the network is untouched — so reaching for it is a mistake worth\n * reporting rather than a silent no-op that makes a spec assert nothing.\n */\nconst liveNetwork = (): NetworkFixture =>\n new Proxy({} as NetworkFixture, {\n get(_target, property) {\n throw new Error(\n `The network fixture is not available in the live profile (tried to use \"${String(property)}\"). Move this expectation into a *.mock.spec.ts file.`,\n );\n },\n });\n\n/**\n * Playwright's own `test`, plus the reused-server guard and nothing else.\n *\n * For a spec that wants no msw network and no seeded session — proving the\n * unauthenticated redirect, say. It exists so that \"this spec needs no\n * fixtures\" cannot quietly mean \"this spec is not checked against the artifact\n * it is running on\": importing `test` straight from `@playwright/test` is what\n * left the account journeys resolving a principal against a live auth-service\n * while claiming to be hermetic.\n */\nexport const baseTest = base.extend<Pick<EntifixE2eFixtures, 'expectedServer'>>(\n {\n expectedServer: [\n async ({ baseURL }, use) => {\n await assertExpectedServerOnce(baseURL);\n await use();\n },\n { auto: true },\n ],\n },\n);\n\n/**\n * Builds the `test` an entifix app e2e project imports.\n *\n * In `mock` it installs the msw handlers over Playwright's `page.route()`\n * (via `@msw/playwright`), so the browser talks to the fixture backend and the\n * app itself is untouched — no worker script, no `NEXT_PUBLIC_*` switch, no\n * mocking code shipped in the application bundle. In `live` the fixture stands\n * down and the same specs hit the real service.\n */\nexport const defineEntifixE2eTest = ({\n handlers = [],\n passthroughOrigins = [],\n}: EntifixE2eTestOptions = {}) =>\n baseTest.extend<EntifixE2eFixtures>({\n handlers: [handlers, { option: true }],\n\n network: [\n async ({ context, handlers: activeHandlers }, use) => {\n if (!isMockProfile()) {\n await use(liveNetwork());\n return;\n }\n\n const network = defineNetworkFixture({\n context,\n handlers: activeHandlers,\n // An unstubbed *service* request must fail the test rather than\n // escape to the network: a spec that passes because it silently\n // talked to nothing is worse than one that fails. The app's own\n // origin is exempt — it legitimately serves itself.\n onUnhandledRequest: (request, print) => {\n if (\n passthroughOrigins.some(origin => request.url.startsWith(origin))\n ) {\n return;\n }\n print.error();\n },\n });\n\n await network.enable();\n await use(network);\n await network.disable();\n },\n { auto: true },\n ],\n });\n\nexport type { NetworkFixture } from '@msw/playwright';\nexport { expect } from '@playwright/test';\n"],"names":["defineNetworkFixture","test","base","isMockProfile","assertExpectedServerOnce","liveNetwork","Proxy","get","_target","property","Error","String","baseTest","extend","expectedServer","baseURL","use","auto","defineEntifixE2eTest","handlers","passthroughOrigins","option","network","context","activeHandlers","onUnhandledRequest","request","print","some","origin","url","startsWith","error","enable","disable","expect"],"mappings":"AAAA,SAASA,oBAAoB,QAA6B,kBAAkB;AAC5E,SAASC,QAAQC,IAAI,QAAQ,mBAAmB;AAGhD,SAASC,aAAa,QAAQ,wBAAwB;AACtD,SAASC,wBAAwB,QAAQ,8BAA8B;AA6BvE;;;CAGC,GACD,MAAMC,cAAc,IAClB,IAAIC,MAAM,CAAC,GAAqB;QAC9BC,KAAIC,OAAO,EAAEC,QAAQ;YACnB,MAAM,IAAIC,MACR,CAAC,wEAAwE,EAAEC,OAAOF,UAAU,qDAAqD,CAAC;QAEtJ;IACF;AAEF;;;;;;;;;CASC,GACD,OAAO,MAAMG,WAAWV,KAAKW,MAAM,CACjC;IACEC,gBAAgB;QACd,OAAO,EAAEC,OAAO,EAAE,EAAEC;YAClB,MAAMZ,yBAAyBW;YAC/B,MAAMC;QACR;QACA;YAAEC,MAAM;QAAK;KACd;AACH,GACA;AAEF;;;;;;;;CAQC,GACD,OAAO,MAAMC,uBAAuB,CAAC,EACnCC,WAAW,EAAE,EACbC,qBAAqB,EAAE,EACD,GAAG,CAAC,CAAC,GAC3BR,SAASC,MAAM,CAAqB;QAClCM,UAAU;YAACA;YAAU;gBAAEE,QAAQ;YAAK;SAAE;QAEtCC,SAAS;YACP,OAAO,EAAEC,OAAO,EAAEJ,UAAUK,cAAc,EAAE,EAAER;gBAC5C,IAAI,CAACb,iBAAiB;oBACpB,MAAMa,IAAIX;oBACV;gBACF;gBAEA,MAAMiB,UAAUtB,qBAAqB;oBACnCuB;oBACAJ,UAAUK;oBACV,gEAAgE;oBAChE,gEAAgE;oBAChE,gEAAgE;oBAChE,oDAAoD;oBACpDC,oBAAoB,CAACC,SAASC;wBAC5B,IACEP,mBAAmBQ,IAAI,CAACC,CAAAA,SAAUH,QAAQI,GAAG,CAACC,UAAU,CAACF,UACzD;4BACA;wBACF;wBACAF,MAAMK,KAAK;oBACb;gBACF;gBAEA,MAAMV,QAAQW,MAAM;gBACpB,MAAMjB,IAAIM;gBACV,MAAMA,QAAQY,OAAO;YACvB;YACA;gBAAEjB,MAAM;YAAK;SACd;IACH,GAAG;AAGL,SAASkB,MAAM,QAAQ,mBAAmB"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * The two ways an e2e suite can be run.
3
+ *
4
+ * - `mock` — hermetic. The system under test is real; everything it talks to is
5
+ * a fake wired in at the transport boundary. Needs no infrastructure, so it
6
+ * runs on every pull request.
7
+ * - `live` — the same journeys against real services and real datastores.
8
+ * Run locally (or against a deployment) once the infra stack is up.
9
+ *
10
+ * The profile is a *run* concern, never a spec concern: a spec asks the fixture
11
+ * for a client or a page and stays identical in both.
12
+ */
13
+ export declare const E2E_PROFILES: readonly ["mock", "live"];
14
+ export type E2eProfile = (typeof E2E_PROFILES)[number];
15
+ /** The environment variable that selects the profile. */
16
+ export declare const E2E_PROFILE_VAR = "E2E_PROFILE";
17
+ /**
18
+ * `mock` is the default because the default has to be the one that runs
19
+ * anywhere: a fresh clone, a CI runner, a laptop with no minikube. Requiring
20
+ * infrastructure to get a green run is how e2e suites end up being skipped.
21
+ */
22
+ export declare const DEFAULT_E2E_PROFILE: E2eProfile;
23
+ /** Just enough of `process.env` to read from, so specs can pass a literal. */
24
+ export type E2eEnv = Record<string, string | undefined>;
25
+ /**
26
+ * Reads the profile from the environment.
27
+ *
28
+ * An unrecognized value throws rather than falling back: `E2E_PROFILE=live-ish`
29
+ * silently running the mock suite is exactly the failure this whole seam exists
30
+ * to prevent.
31
+ */
32
+ export declare const resolveE2eProfile: (env?: E2eEnv) => E2eProfile;
33
+ export declare const isMockProfile: (env?: E2eEnv) => boolean;
34
+ export declare const isLiveProfile: (env?: E2eEnv) => boolean;
35
+ /**
36
+ * The base URL a `live` run must be pointed at, or a failure explaining how.
37
+ *
38
+ * It **throws**; it does not skip. A suite that skips itself when its target is
39
+ * missing reports green for a run that tested nothing, which is how a broken
40
+ * live environment stays unnoticed.
41
+ */
42
+ export declare const requireLiveUrl: (variable: string, env?: E2eEnv) => string;
43
+ //# sourceMappingURL=profile.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"profile.d.ts","sourceRoot":"","sources":["../../src/profile/profile.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,YAAY,2BAA4B,CAAC;AAEtD,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,YAAY,CAAC,CAAC,MAAM,CAAC,CAAC;AAEvD,yDAAyD;AACzD,eAAO,MAAM,eAAe,gBAAgB,CAAC;AAE7C;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,EAAE,UAAmB,CAAC;AAEtD,8EAA8E;AAC9E,MAAM,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;AAKxD;;;;;;GAMG;AACH,eAAO,MAAM,iBAAiB,GAAI,MAAK,MAAoB,KAAG,UAQ7D,CAAC;AAEF,eAAO,MAAM,aAAa,GAAI,MAAM,MAAM,KAAG,OACV,CAAC;AAEpC,eAAO,MAAM,aAAa,GAAI,MAAM,MAAM,KAAG,OACV,CAAC;AAEpC;;;;;;GAMG;AACH,eAAO,MAAM,cAAc,GACzB,UAAU,MAAM,EAChB,MAAK,MAAoB,KACxB,MAQF,CAAC"}
@@ -0,0 +1,51 @@
1
+ /**
2
+ * The two ways an e2e suite can be run.
3
+ *
4
+ * - `mock` — hermetic. The system under test is real; everything it talks to is
5
+ * a fake wired in at the transport boundary. Needs no infrastructure, so it
6
+ * runs on every pull request.
7
+ * - `live` — the same journeys against real services and real datastores.
8
+ * Run locally (or against a deployment) once the infra stack is up.
9
+ *
10
+ * The profile is a *run* concern, never a spec concern: a spec asks the fixture
11
+ * for a client or a page and stays identical in both.
12
+ */ export const E2E_PROFILES = [
13
+ 'mock',
14
+ 'live'
15
+ ];
16
+ /** The environment variable that selects the profile. */ export const E2E_PROFILE_VAR = 'E2E_PROFILE';
17
+ /**
18
+ * `mock` is the default because the default has to be the one that runs
19
+ * anywhere: a fresh clone, a CI runner, a laptop with no minikube. Requiring
20
+ * infrastructure to get a green run is how e2e suites end up being skipped.
21
+ */ export const DEFAULT_E2E_PROFILE = 'mock';
22
+ const isProfile = (value)=>E2E_PROFILES.includes(value);
23
+ /**
24
+ * Reads the profile from the environment.
25
+ *
26
+ * An unrecognized value throws rather than falling back: `E2E_PROFILE=live-ish`
27
+ * silently running the mock suite is exactly the failure this whole seam exists
28
+ * to prevent.
29
+ */ export const resolveE2eProfile = (env = process.env)=>{
30
+ const raw = env[E2E_PROFILE_VAR];
31
+ if (raw === undefined || raw === '') return DEFAULT_E2E_PROFILE;
32
+ if (isProfile(raw)) return raw;
33
+ throw new Error(`Unknown ${E2E_PROFILE_VAR} "${raw}". Expected one of: ${E2E_PROFILES.join(', ')}.`);
34
+ };
35
+ export const isMockProfile = (env)=>resolveE2eProfile(env) === 'mock';
36
+ export const isLiveProfile = (env)=>resolveE2eProfile(env) === 'live';
37
+ /**
38
+ * The base URL a `live` run must be pointed at, or a failure explaining how.
39
+ *
40
+ * It **throws**; it does not skip. A suite that skips itself when its target is
41
+ * missing reports green for a run that tested nothing, which is how a broken
42
+ * live environment stays unnoticed.
43
+ */ export const requireLiveUrl = (variable, env = process.env)=>{
44
+ const value = env[variable];
45
+ if (value === undefined || value === '') {
46
+ throw new Error(`${E2E_PROFILE_VAR}=live requires ${variable} to point at the running service, e.g. ${variable}=http://localhost:3101.`);
47
+ }
48
+ return value.replace(/\/+$/, '');
49
+ };
50
+
51
+ //# sourceMappingURL=profile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/profile/profile.ts"],"sourcesContent":["/**\n * The two ways an e2e suite can be run.\n *\n * - `mock` — hermetic. The system under test is real; everything it talks to is\n * a fake wired in at the transport boundary. Needs no infrastructure, so it\n * runs on every pull request.\n * - `live` — the same journeys against real services and real datastores.\n * Run locally (or against a deployment) once the infra stack is up.\n *\n * The profile is a *run* concern, never a spec concern: a spec asks the fixture\n * for a client or a page and stays identical in both.\n */\nexport const E2E_PROFILES = ['mock', 'live'] as const;\n\nexport type E2eProfile = (typeof E2E_PROFILES)[number];\n\n/** The environment variable that selects the profile. */\nexport const E2E_PROFILE_VAR = 'E2E_PROFILE';\n\n/**\n * `mock` is the default because the default has to be the one that runs\n * anywhere: a fresh clone, a CI runner, a laptop with no minikube. Requiring\n * infrastructure to get a green run is how e2e suites end up being skipped.\n */\nexport const DEFAULT_E2E_PROFILE: E2eProfile = 'mock';\n\n/** Just enough of `process.env` to read from, so specs can pass a literal. */\nexport type E2eEnv = Record<string, string | undefined>;\n\nconst isProfile = (value: string): value is E2eProfile =>\n (E2E_PROFILES as readonly string[]).includes(value);\n\n/**\n * Reads the profile from the environment.\n *\n * An unrecognized value throws rather than falling back: `E2E_PROFILE=live-ish`\n * silently running the mock suite is exactly the failure this whole seam exists\n * to prevent.\n */\nexport const resolveE2eProfile = (env: E2eEnv = process.env): E2eProfile => {\n const raw = env[E2E_PROFILE_VAR];\n if (raw === undefined || raw === '') return DEFAULT_E2E_PROFILE;\n if (isProfile(raw)) return raw;\n\n throw new Error(\n `Unknown ${E2E_PROFILE_VAR} \"${raw}\". Expected one of: ${E2E_PROFILES.join(', ')}.`,\n );\n};\n\nexport const isMockProfile = (env?: E2eEnv): boolean =>\n resolveE2eProfile(env) === 'mock';\n\nexport const isLiveProfile = (env?: E2eEnv): boolean =>\n resolveE2eProfile(env) === 'live';\n\n/**\n * The base URL a `live` run must be pointed at, or a failure explaining how.\n *\n * It **throws**; it does not skip. A suite that skips itself when its target is\n * missing reports green for a run that tested nothing, which is how a broken\n * live environment stays unnoticed.\n */\nexport const requireLiveUrl = (\n variable: string,\n env: E2eEnv = process.env,\n): string => {\n const value = env[variable];\n if (value === undefined || value === '') {\n throw new Error(\n `${E2E_PROFILE_VAR}=live requires ${variable} to point at the running service, e.g. ${variable}=http://localhost:3101.`,\n );\n }\n return value.replace(/\\/+$/, '');\n};\n"],"names":["E2E_PROFILES","E2E_PROFILE_VAR","DEFAULT_E2E_PROFILE","isProfile","value","includes","resolveE2eProfile","env","process","raw","undefined","Error","join","isMockProfile","isLiveProfile","requireLiveUrl","variable","replace"],"mappings":"AAAA;;;;;;;;;;;CAWC,GACD,OAAO,MAAMA,eAAe;IAAC;IAAQ;CAAO,CAAU;AAItD,uDAAuD,GACvD,OAAO,MAAMC,kBAAkB,cAAc;AAE7C;;;;CAIC,GACD,OAAO,MAAMC,sBAAkC,OAAO;AAKtD,MAAMC,YAAY,CAACC,QACjB,AAACJ,aAAmCK,QAAQ,CAACD;AAE/C;;;;;;CAMC,GACD,OAAO,MAAME,oBAAoB,CAACC,MAAcC,QAAQD,GAAG;IACzD,MAAME,MAAMF,GAAG,CAACN,gBAAgB;IAChC,IAAIQ,QAAQC,aAAaD,QAAQ,IAAI,OAAOP;IAC5C,IAAIC,UAAUM,MAAM,OAAOA;IAE3B,MAAM,IAAIE,MACR,CAAC,QAAQ,EAAEV,gBAAgB,EAAE,EAAEQ,IAAI,oBAAoB,EAAET,aAAaY,IAAI,CAAC,MAAM,CAAC,CAAC;AAEvF,EAAE;AAEF,OAAO,MAAMC,gBAAgB,CAACN,MAC5BD,kBAAkBC,SAAS,OAAO;AAEpC,OAAO,MAAMO,gBAAgB,CAACP,MAC5BD,kBAAkBC,SAAS,OAAO;AAEpC;;;;;;CAMC,GACD,OAAO,MAAMQ,iBAAiB,CAC5BC,UACAT,MAAcC,QAAQD,GAAG;IAEzB,MAAMH,QAAQG,GAAG,CAACS,SAAS;IAC3B,IAAIZ,UAAUM,aAAaN,UAAU,IAAI;QACvC,MAAM,IAAIO,MACR,GAAGV,gBAAgB,eAAe,EAAEe,SAAS,uCAAuC,EAAEA,SAAS,uBAAuB,CAAC;IAE3H;IACA,OAAOZ,MAAMa,OAAO,CAAC,QAAQ;AAC/B,EAAE"}
@@ -0,0 +1,54 @@
1
+ import { type AxiosInstance } from 'axios';
2
+ import { type E2eProfile } from '../profile/profile.js';
3
+ /** A service booted for the run, whichever profile started it. */
4
+ export interface RunningService {
5
+ readonly baseUrl: string;
6
+ close(): Promise<void>;
7
+ }
8
+ export interface ServiceE2eOptions {
9
+ /**
10
+ * The variable a `live` run reads its target from, e.g.
11
+ * `MARKETPLACE_ADMIN_SERVICE_URL`.
12
+ */
13
+ liveUrlEnvVar: string;
14
+ /**
15
+ * Boots the service in-process for a `mock` run. Supplied by the e2e project
16
+ * rather than by this package, because only the project knows its own
17
+ * `router` and which fakes stand in for its infrastructure — and because a
18
+ * package under `entifix` must not depend on a `shells` one.
19
+ */
20
+ startMock: () => Promise<RunningService>;
21
+ /**
22
+ * Default `Authorization` header for every request the suite makes, built
23
+ * once at start-up.
24
+ *
25
+ * A guarded service answers `401` to an anonymous request, which would turn
26
+ * every journey in the suite into an authentication test. Supplying a
27
+ * principal here keeps the specs about what they are actually asserting;
28
+ * the guard itself is covered by its own spec, which omits or corrupts the
29
+ * header on purpose.
30
+ */
31
+ authorization?: () => Promise<string>;
32
+ }
33
+ /**
34
+ * The handle a service e2e spec works through. `client` and `baseUrl` are only
35
+ * valid once the suite has started, so both are getters that fail loudly rather
36
+ * than handing out an empty string.
37
+ */
38
+ export interface ServiceE2eContext {
39
+ readonly profile: E2eProfile;
40
+ readonly baseUrl: string;
41
+ readonly client: AxiosInstance;
42
+ }
43
+ /**
44
+ * Wires a service e2e suite to the active profile.
45
+ *
46
+ * In `mock` the service is booted in-process against driver fakes; in `live` it
47
+ * is the process already running on `liveUrlEnvVar`. Either way the spec gets
48
+ * the same axios client against the same routes, so ONE suite covers both — no
49
+ * `test.skip` on an env var, and no second copy of the journeys.
50
+ *
51
+ * Call it at the top level of a spec file; it registers the suite hooks itself.
52
+ */
53
+ export declare const defineServiceE2e: ({ liveUrlEnvVar, startMock, authorization, }: ServiceE2eOptions) => ServiceE2eContext;
54
+ //# sourceMappingURL=define-service-e2e.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"define-service-e2e.d.ts","sourceRoot":"","sources":["../../src/service/define-service-e2e.ts"],"names":[],"mappings":"AAAA,OAAc,EAAE,KAAK,aAAa,EAAE,MAAM,OAAO,CAAC;AAGlD,OAAO,EACL,KAAK,UAAU,EAGhB,MAAM,uBAAuB,CAAC;AAE/B,kEAAkE;AAClE,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED,MAAM,WAAW,iBAAiB;IAChC;;;OAGG;IACH,aAAa,EAAE,MAAM,CAAC;IACtB;;;;;OAKG;IACH,SAAS,EAAE,MAAM,OAAO,CAAC,cAAc,CAAC,CAAC;IACzC;;;;;;;;;OASG;IACH,aAAa,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;CACvC;AAED;;;;GAIG;AACH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAC;IAC7B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC;CAChC;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,gBAAgB,GAAI,8CAI9B,iBAAiB,KAAG,iBAoDtB,CAAC"}
@@ -0,0 +1,57 @@
1
+ import axios from 'axios';
2
+ import { afterAll, beforeAll } from 'vitest';
3
+ import { requireLiveUrl, resolveE2eProfile } from '../profile/profile.js';
4
+ /**
5
+ * Wires a service e2e suite to the active profile.
6
+ *
7
+ * In `mock` the service is booted in-process against driver fakes; in `live` it
8
+ * is the process already running on `liveUrlEnvVar`. Either way the spec gets
9
+ * the same axios client against the same routes, so ONE suite covers both — no
10
+ * `test.skip` on an env var, and no second copy of the journeys.
11
+ *
12
+ * Call it at the top level of a spec file; it registers the suite hooks itself.
13
+ */ export const defineServiceE2e = ({ liveUrlEnvVar, startMock, authorization })=>{
14
+ const profile = resolveE2eProfile();
15
+ let running;
16
+ let client;
17
+ const started = ()=>{
18
+ if (running === undefined) {
19
+ throw new Error('The e2e service is not running yet — read `baseUrl`/`client` inside a test, not at module scope.');
20
+ }
21
+ return running;
22
+ };
23
+ beforeAll(async ()=>{
24
+ running = profile === 'mock' ? await startMock() : {
25
+ baseUrl: requireLiveUrl(liveUrlEnvVar),
26
+ // Nothing to tear down: the live process outlives the run.
27
+ close: ()=>Promise.resolve()
28
+ };
29
+ client = axios.create({
30
+ baseURL: running.baseUrl,
31
+ headers: authorization === undefined ? undefined : {
32
+ Authorization: await authorization()
33
+ },
34
+ // 4xx and 5xx are assertion subjects in these suites, not transport
35
+ // failures — a service e2e that cannot assert on a 400 is missing half
36
+ // its surface.
37
+ validateStatus: ()=>true
38
+ });
39
+ });
40
+ afterAll(async ()=>{
41
+ await (running == null ? void 0 : running.close());
42
+ running = undefined;
43
+ client = undefined;
44
+ });
45
+ return {
46
+ profile,
47
+ get baseUrl () {
48
+ return started().baseUrl;
49
+ },
50
+ get client () {
51
+ started();
52
+ return client;
53
+ }
54
+ };
55
+ };
56
+
57
+ //# sourceMappingURL=define-service-e2e.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/service/define-service-e2e.ts"],"sourcesContent":["import axios, { type AxiosInstance } from 'axios';\nimport { afterAll, beforeAll } from 'vitest';\n\nimport {\n type E2eProfile,\n requireLiveUrl,\n resolveE2eProfile,\n} from '../profile/profile.js';\n\n/** A service booted for the run, whichever profile started it. */\nexport interface RunningService {\n readonly baseUrl: string;\n close(): Promise<void>;\n}\n\nexport interface ServiceE2eOptions {\n /**\n * The variable a `live` run reads its target from, e.g.\n * `MARKETPLACE_ADMIN_SERVICE_URL`.\n */\n liveUrlEnvVar: string;\n /**\n * Boots the service in-process for a `mock` run. Supplied by the e2e project\n * rather than by this package, because only the project knows its own\n * `router` and which fakes stand in for its infrastructure — and because a\n * package under `entifix` must not depend on a `shells` one.\n */\n startMock: () => Promise<RunningService>;\n /**\n * Default `Authorization` header for every request the suite makes, built\n * once at start-up.\n *\n * A guarded service answers `401` to an anonymous request, which would turn\n * every journey in the suite into an authentication test. Supplying a\n * principal here keeps the specs about what they are actually asserting;\n * the guard itself is covered by its own spec, which omits or corrupts the\n * header on purpose.\n */\n authorization?: () => Promise<string>;\n}\n\n/**\n * The handle a service e2e spec works through. `client` and `baseUrl` are only\n * valid once the suite has started, so both are getters that fail loudly rather\n * than handing out an empty string.\n */\nexport interface ServiceE2eContext {\n readonly profile: E2eProfile;\n readonly baseUrl: string;\n readonly client: AxiosInstance;\n}\n\n/**\n * Wires a service e2e suite to the active profile.\n *\n * In `mock` the service is booted in-process against driver fakes; in `live` it\n * is the process already running on `liveUrlEnvVar`. Either way the spec gets\n * the same axios client against the same routes, so ONE suite covers both — no\n * `test.skip` on an env var, and no second copy of the journeys.\n *\n * Call it at the top level of a spec file; it registers the suite hooks itself.\n */\nexport const defineServiceE2e = ({\n liveUrlEnvVar,\n startMock,\n authorization,\n}: ServiceE2eOptions): ServiceE2eContext => {\n const profile = resolveE2eProfile();\n let running: RunningService | undefined;\n let client: AxiosInstance | undefined;\n\n const started = (): RunningService => {\n if (running === undefined) {\n throw new Error(\n 'The e2e service is not running yet — read `baseUrl`/`client` inside a test, not at module scope.',\n );\n }\n return running;\n };\n\n beforeAll(async () => {\n running =\n profile === 'mock'\n ? await startMock()\n : {\n baseUrl: requireLiveUrl(liveUrlEnvVar),\n // Nothing to tear down: the live process outlives the run.\n close: () => Promise.resolve(),\n };\n client = axios.create({\n baseURL: running.baseUrl,\n headers:\n authorization === undefined\n ? undefined\n : { Authorization: await authorization() },\n // 4xx and 5xx are assertion subjects in these suites, not transport\n // failures — a service e2e that cannot assert on a 400 is missing half\n // its surface.\n validateStatus: () => true,\n });\n });\n\n afterAll(async () => {\n await running?.close();\n running = undefined;\n client = undefined;\n });\n\n return {\n profile,\n get baseUrl() {\n return started().baseUrl;\n },\n get client() {\n started();\n return client as AxiosInstance;\n },\n };\n};\n"],"names":["axios","afterAll","beforeAll","requireLiveUrl","resolveE2eProfile","defineServiceE2e","liveUrlEnvVar","startMock","authorization","profile","running","client","started","undefined","Error","baseUrl","close","Promise","resolve","create","baseURL","headers","Authorization","validateStatus"],"mappings":"AAAA,OAAOA,WAAmC,QAAQ;AAClD,SAASC,QAAQ,EAAEC,SAAS,QAAQ,SAAS;AAE7C,SAEEC,cAAc,EACdC,iBAAiB,QACZ,wBAAwB;AA6C/B;;;;;;;;;CASC,GACD,OAAO,MAAMC,mBAAmB,CAAC,EAC/BC,aAAa,EACbC,SAAS,EACTC,aAAa,EACK;IAClB,MAAMC,UAAUL;IAChB,IAAIM;IACJ,IAAIC;IAEJ,MAAMC,UAAU;QACd,IAAIF,YAAYG,WAAW;YACzB,MAAM,IAAIC,MACR;QAEJ;QACA,OAAOJ;IACT;IAEAR,UAAU;QACRQ,UACED,YAAY,SACR,MAAMF,cACN;YACEQ,SAASZ,eAAeG;YACxB,2DAA2D;YAC3DU,OAAO,IAAMC,QAAQC,OAAO;QAC9B;QACNP,SAASX,MAAMmB,MAAM,CAAC;YACpBC,SAASV,QAAQK,OAAO;YACxBM,SACEb,kBAAkBK,YACdA,YACA;gBAAES,eAAe,MAAMd;YAAgB;YAC7C,oEAAoE;YACpE,uEAAuE;YACvE,eAAe;YACfe,gBAAgB,IAAM;QACxB;IACF;IAEAtB,SAAS;QACP,OAAMS,2BAAAA,QAASM,KAAK;QACpBN,UAAUG;QACVF,SAASE;IACX;IAEA,OAAO;QACLJ;QACA,IAAIM,WAAU;YACZ,OAAOH,UAAUG,OAAO;QAC1B;QACA,IAAIJ,UAAS;YACXC;YACA,OAAOD;QACT;IACF;AACF,EAAE"}
@@ -0,0 +1,2 @@
1
+ export * from './define-service-e2e.js';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/service/index.ts"],"names":[],"mappings":"AAAA,cAAc,yBAAyB,CAAC"}
@@ -0,0 +1,3 @@
1
+ export * from './define-service-e2e.js';
2
+
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/service/index.ts"],"sourcesContent":["export * from './define-service-e2e.js';\n"],"names":[],"mappings":"AAAA,cAAc,0BAA0B"}
package/package.json ADDED
@@ -0,0 +1,131 @@
1
+ {
2
+ "name": "@entifix/testing-e2e",
3
+ "version": "0.1.0-0",
4
+ "license": "MIT",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git+https://github.com/r10c-technologies/entifix.git",
8
+ "directory": "packages/ts/testing-e2e"
9
+ },
10
+ "publishConfig": {
11
+ "access": "public"
12
+ },
13
+ "main": "./dist/index.js",
14
+ "module": "./dist/index.js",
15
+ "types": "./dist/index.d.ts",
16
+ "files": [
17
+ "dist",
18
+ "!**/*.tsbuildinfo",
19
+ "LICENSE"
20
+ ],
21
+ "type": "module",
22
+ "exports": {
23
+ "./package.json": "./package.json",
24
+ ".": {
25
+ "@entifix/source": "./src/index.ts",
26
+ "types": "./dist/index.d.ts",
27
+ "import": "./dist/index.js",
28
+ "default": "./dist/index.js"
29
+ },
30
+ "./fixtures": {
31
+ "@entifix/source": "./src/fixtures/index.ts",
32
+ "types": "./dist/fixtures/index.d.ts",
33
+ "import": "./dist/fixtures/index.js",
34
+ "default": "./dist/fixtures/index.js"
35
+ },
36
+ "./service": {
37
+ "@entifix/source": "./src/service/index.ts",
38
+ "types": "./dist/service/index.d.ts",
39
+ "import": "./dist/service/index.js",
40
+ "default": "./dist/service/index.js"
41
+ },
42
+ "./playwright": {
43
+ "@entifix/source": "./src/playwright/index.ts",
44
+ "types": "./dist/playwright/index.d.ts",
45
+ "import": "./dist/playwright/index.js",
46
+ "default": "./dist/playwright/index.js"
47
+ }
48
+ },
49
+ "nx": {
50
+ "tags": [
51
+ "layer:entifix",
52
+ "scope:shared",
53
+ "type:testing",
54
+ "tier:5"
55
+ ],
56
+ "sourceRoot": "packages/ts/testing-e2e/src",
57
+ "targets": {
58
+ "build": {
59
+ "executor": "@nx/js:swc",
60
+ "outputs": [
61
+ "{options.outputPath}"
62
+ ],
63
+ "options": {
64
+ "outputPath": "packages/ts/testing-e2e/dist",
65
+ "main": "packages/ts/testing-e2e/src/index.ts",
66
+ "tsConfig": "packages/ts/testing-e2e/tsconfig.lib.json",
67
+ "skipTypeCheck": true,
68
+ "stripLeadingPaths": true
69
+ }
70
+ }
71
+ }
72
+ },
73
+ "dependencies": {
74
+ "@swc/helpers": "0.5.23",
75
+ "@entifix/business": "0.1.0-0",
76
+ "@entifix/testing-unit": "0.1.0-0",
77
+ "@entifix/core": "0.1.0-0"
78
+ },
79
+ "peerDependencies": {
80
+ "@nx/devkit": ">=23 <24",
81
+ "@nx/playwright": ">=23 <24",
82
+ "@playwright/test": "*",
83
+ "axios": "*",
84
+ "effect": ">=3.22 <4",
85
+ "msw": "*",
86
+ "vitest": "*",
87
+ "@entifix/amqp": "0.1.0-0",
88
+ "@entifix/redis": "0.1.0-0",
89
+ "@entifix/mongo": "0.1.0-0"
90
+ },
91
+ "peerDependenciesMeta": {
92
+ "@msw/playwright": {
93
+ "optional": true
94
+ },
95
+ "@nx/devkit": {
96
+ "optional": true
97
+ },
98
+ "@nx/playwright": {
99
+ "optional": true
100
+ },
101
+ "@playwright/test": {
102
+ "optional": true
103
+ },
104
+ "@entifix/amqp": {
105
+ "optional": true
106
+ },
107
+ "@entifix/mongo": {
108
+ "optional": true
109
+ },
110
+ "@entifix/redis": {
111
+ "optional": true
112
+ },
113
+ "axios": {
114
+ "optional": true
115
+ },
116
+ "msw": {
117
+ "optional": true
118
+ },
119
+ "vitest": {
120
+ "optional": true
121
+ }
122
+ },
123
+ "devDependencies": {
124
+ "@nx/devkit": "23.0.1",
125
+ "@nx/playwright": "23.0.1",
126
+ "effect": "3.22.1",
127
+ "@entifix/amqp": "0.1.0-0",
128
+ "@entifix/mongo": "0.1.0-0",
129
+ "@entifix/redis": "0.1.0-0"
130
+ }
131
+ }