scenario-mock-server
Advanced tools
Comparing version 1.1.0 to 1.2.0
import * as server_with_kill from 'server-with-kill'; | ||
import express from 'express'; | ||
type Scenario = { | ||
type ScenarioWithOptionalProperties = { | ||
name?: string; | ||
@@ -10,3 +10,6 @@ description?: string; | ||
extend?: string; | ||
} | Mock[]; | ||
group?: string; | ||
}; | ||
type Scenario = ScenarioWithOptionalProperties | Mock[]; | ||
type Groups = Record<string, string>; | ||
type ApiScenario = { | ||
@@ -17,2 +20,3 @@ id: string; | ||
selected: boolean; | ||
group: null | string; | ||
}; | ||
@@ -66,2 +70,3 @@ type ScenarioMap = Record<string, Scenario>; | ||
scenariosPath?: string; | ||
groupsPath?: string; | ||
cookieMode?: boolean; | ||
@@ -74,12 +79,14 @@ parallelContextSize?: number; | ||
declare function run({ scenarios, options, }: { | ||
declare function run({ scenarios, options, groups, }: { | ||
scenarios: ScenarioMap; | ||
options?: Options; | ||
groups?: Groups; | ||
}): server_with_kill.ServerWithKill; | ||
declare function createExpressApp({ scenarios: externalScenarioMap, options, }: { | ||
declare function createExpressApp({ scenarios: externalScenarioMap, options, groups, }: { | ||
scenarios: ScenarioMap; | ||
options?: Omit<Options, 'port'>; | ||
groups?: Groups; | ||
}): ReturnType<typeof express>; | ||
export { ApiScenario, GraphQlMock, HttpMock, Mock, Operation, Options, Scenario, createExpressApp, run }; |
@@ -82,5 +82,7 @@ "use strict"; | ||
uiPath, | ||
scenarios | ||
scenarios, | ||
groups | ||
}) { | ||
return /* @__PURE__ */ import_react.default.createElement("html", { lang: "en" }, /* @__PURE__ */ import_react.default.createElement("head", null, /* @__PURE__ */ import_react.default.createElement("meta", { charSet: "utf-8" }), /* @__PURE__ */ import_react.default.createElement("meta", { name: "viewport", content: "width=device-width,initial-scale=1" }), /* @__PURE__ */ import_react.default.createElement("title", null, updatedScenarioName ? "Updated - " : "", "Scenarios - Scenario Mock Server"), /* @__PURE__ */ import_react.default.createElement( | ||
return /* @__PURE__ */ import_react.default.createElement("html", { lang: "en" }, /* @__PURE__ */ import_react.default.createElement("head", null, /* @__PURE__ */ import_react.default.createElement("meta", { charSet: "utf-8" }), /* @__PURE__ */ import_react.default.createElement("meta", { name: "viewport", content: "width=device-width,initial-scale=1" }), /* @__PURE__ */ import_react.default.createElement("title", null, `${updatedScenarioName ? "Updated - " : ""}Scenarios - Scenario Mock | ||
Server`), /* @__PURE__ */ import_react.default.createElement( | ||
"link", | ||
@@ -91,12 +93,3 @@ { | ||
} | ||
)), /* @__PURE__ */ import_react.default.createElement("body", null, /* @__PURE__ */ import_react.default.createElement("main", null, /* @__PURE__ */ import_react.default.createElement(ScenarioUpdateInfo, { updatedScenarioName }), /* @__PURE__ */ import_react.default.createElement("form", { className: "stack-1", method: "POST", action: uiPath }, /* @__PURE__ */ import_react.default.createElement("p", null, /* @__PURE__ */ import_react.default.createElement("a", { href: uiPath }, "Refresh page")), /* @__PURE__ */ import_react.default.createElement(CallToActionButton, null), /* @__PURE__ */ import_react.default.createElement("fieldset", { className: "stack-3" }, /* @__PURE__ */ import_react.default.createElement("legend", null, /* @__PURE__ */ import_react.default.createElement("h1", null, "Scenarios")), /* @__PURE__ */ import_react.default.createElement("div", { className: "stack-3" }, scenarios.map((scenario) => /* @__PURE__ */ import_react.default.createElement("div", { key: scenario.id }, /* @__PURE__ */ import_react.default.createElement( | ||
"input", | ||
{ | ||
type: "radio", | ||
id: scenario.id, | ||
name: "scenarioId", | ||
value: scenario.id, | ||
defaultChecked: scenario.selected | ||
} | ||
), /* @__PURE__ */ import_react.default.createElement("label", { htmlFor: scenario.id }, scenario.name), scenario.description ? /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, /* @__PURE__ */ import_react.default.createElement("br", null), /* @__PURE__ */ import_react.default.createElement("details", null, /* @__PURE__ */ import_react.default.createElement("summary", null, "Description"), /* @__PURE__ */ import_react.default.createElement("div", { className: "description" }, scenario.description))) : null)))), /* @__PURE__ */ import_react.default.createElement(CallToActionButton, null))))); | ||
)), /* @__PURE__ */ import_react.default.createElement("body", null, /* @__PURE__ */ import_react.default.createElement("main", null, /* @__PURE__ */ import_react.default.createElement(ScenarioUpdateInfo, { updatedScenarioName }), /* @__PURE__ */ import_react.default.createElement("form", { className: "stack-1", method: "POST", action: uiPath }, /* @__PURE__ */ import_react.default.createElement("p", null, /* @__PURE__ */ import_react.default.createElement("a", { href: uiPath }, "Refresh page")), /* @__PURE__ */ import_react.default.createElement(CallToActionButton, null), /* @__PURE__ */ import_react.default.createElement("fieldset", { className: "stack-3" }, /* @__PURE__ */ import_react.default.createElement("legend", null, /* @__PURE__ */ import_react.default.createElement("h1", null, "Scenarios")), scenarios.some(({ group }) => group !== null) ? /* @__PURE__ */ import_react.default.createElement(GroupedScenarios, { groups, scenarios }) : /* @__PURE__ */ import_react.default.createElement(ScenarioList, { scenarios })), /* @__PURE__ */ import_react.default.createElement(CallToActionButton, null))))); | ||
} | ||
@@ -114,2 +107,32 @@ function ScenarioUpdateInfo({ | ||
} | ||
var NULL_GROUP_ID = "sms-other"; | ||
function GroupedScenarios({ | ||
groups, | ||
scenarios | ||
}) { | ||
const groupedScenarios = {}; | ||
scenarios.forEach((scenario) => { | ||
const group = scenario.group === null ? NULL_GROUP_ID : scenario.group; | ||
groupedScenarios[group] = groupedScenarios[group] || []; | ||
groupedScenarios[group].push(scenario); | ||
}); | ||
const groupsWithLabelIds = Object.keys(groups); | ||
const groupsWithoutLabelIds = Object.keys(groupedScenarios).filter( | ||
(groupId) => !groupsWithLabelIds.includes(groupId) && groupId !== NULL_GROUP_ID | ||
); | ||
const groupEntries = Object.entries(groups).concat(groupsWithoutLabelIds.map((groupId) => [groupId, groupId])).concat([[NULL_GROUP_ID, "Other"]]); | ||
return /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, groupEntries.map(([groupId, groupName]) => /* @__PURE__ */ import_react.default.createElement("div", { key: groupId }, /* @__PURE__ */ import_react.default.createElement("h2", null, groupName), /* @__PURE__ */ import_react.default.createElement(ScenarioList, { scenarios: groupedScenarios[groupId] })))); | ||
} | ||
function ScenarioList({ scenarios }) { | ||
return /* @__PURE__ */ import_react.default.createElement("div", { className: "stack-3" }, scenarios.map((scenario) => /* @__PURE__ */ import_react.default.createElement("div", { key: scenario.id }, /* @__PURE__ */ import_react.default.createElement( | ||
"input", | ||
{ | ||
type: "radio", | ||
id: scenario.id, | ||
name: "scenarioId", | ||
value: scenario.id, | ||
defaultChecked: scenario.selected | ||
} | ||
), /* @__PURE__ */ import_react.default.createElement("label", { htmlFor: scenario.id }, scenario.name), scenario.description ? /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, /* @__PURE__ */ import_react.default.createElement("br", null), /* @__PURE__ */ import_react.default.createElement("details", null, /* @__PURE__ */ import_react.default.createElement("summary", null, "Description"), /* @__PURE__ */ import_react.default.createElement("div", { className: "description" }, scenario.description))) : null))); | ||
} | ||
@@ -209,7 +232,8 @@ // src/cookies.ts | ||
const allScenarios = scenarios.map( | ||
({ id, name, description }) => ({ | ||
({ id, name, description, group }) => ({ | ||
id, | ||
name, | ||
description: description === void 0 ? null : description, | ||
selected: id === scenarioId | ||
selected: id === scenarioId, | ||
group: group === void 0 ? null : group | ||
}) | ||
@@ -307,3 +331,4 @@ ); | ||
setCookie, | ||
getServerScenarioId | ||
getServerScenarioId, | ||
groups | ||
}) { | ||
@@ -319,3 +344,5 @@ const { data } = getScenarios({ | ||
}); | ||
const html = (0, import_server.renderToStaticMarkup)(/* @__PURE__ */ import_react2.default.createElement(Html, { uiPath, scenarios: data })); | ||
const html = (0, import_server.renderToStaticMarkup)( | ||
/* @__PURE__ */ import_react2.default.createElement(Html, { uiPath, scenarios: data, groups }) | ||
); | ||
return "<!DOCTYPE html>\n" + html; | ||
@@ -331,3 +358,4 @@ } | ||
setServerContext, | ||
setServerScenarioId | ||
setServerScenarioId, | ||
groups | ||
}) { | ||
@@ -343,7 +371,8 @@ const updatedScenarioName = scenarioMap[scenarioId].name; | ||
}); | ||
const allScenarios = scenarios.map(({ id, name, description }) => ({ | ||
const allScenarios = scenarios.map(({ id, name, description, group }) => ({ | ||
id, | ||
name, | ||
description: description === void 0 ? null : description, | ||
selected: id === scenarioId | ||
selected: id === scenarioId, | ||
group: group === void 0 ? null : group | ||
})); | ||
@@ -356,2 +385,3 @@ const html = (0, import_server.renderToStaticMarkup)( | ||
scenarios: allScenarios, | ||
groups, | ||
updatedScenarioName | ||
@@ -762,5 +792,28 @@ } | ||
// src/express.ts | ||
function scenarioHasGroup(scenario) { | ||
return !Array.isArray(scenario) && scenario.group != null; | ||
} | ||
function validateAllGroupsHaveNames({ | ||
scenarios, | ||
groups | ||
}) { | ||
const uniqueGroups = Array.from( | ||
Object.values(scenarios).filter(scenarioHasGroup).reduce((set, { group }) => { | ||
set.add(group); | ||
return set; | ||
}, /* @__PURE__ */ new Set()).values() | ||
); | ||
const groupsWithoutNames = uniqueGroups.filter((group) => !groups[group]); | ||
if (groupsWithoutNames.length > 0) { | ||
console.warn( | ||
`The following groups do not have a name: ${groupsWithoutNames.join( | ||
", " | ||
)}` | ||
); | ||
} | ||
} | ||
function createExpressApp({ | ||
scenarios: externalScenarioMap, | ||
options = {} | ||
options = {}, | ||
groups = {} | ||
}) { | ||
@@ -771,5 +824,7 @@ const { | ||
scenariosPath = "/scenarios", | ||
groupsPath = "/groups", | ||
cookieMode = false, | ||
parallelContextSize = 10 | ||
} = options; | ||
validateAllGroupsHaveNames({ scenarios: externalScenarioMap, groups }); | ||
const { scenarios, scenarioMap } = generateScenarios(externalScenarioMap); | ||
@@ -799,3 +854,4 @@ const { initialScenarioId, initialContext } = generatInitialValues( | ||
getServerScenarioId, | ||
scenarios | ||
scenarios, | ||
groups | ||
}); | ||
@@ -815,3 +871,4 @@ res.send(html); | ||
setServerContext, | ||
setServerScenarioId | ||
setServerScenarioId, | ||
groups | ||
}); | ||
@@ -844,2 +901,11 @@ res.send(html); | ||
}); | ||
app.get(groupsPath, (_, res) => { | ||
expressResponse(res, { | ||
status: 200, | ||
headers: { | ||
"content-type": "application/json" | ||
}, | ||
data: Object.entries(groups).map(([id, name]) => ({ id, name })) | ||
}); | ||
}); | ||
app.use(async (req, res) => { | ||
@@ -945,3 +1011,4 @@ const internalRequest = { | ||
scenarios, | ||
options = {} | ||
options = {}, | ||
groups = {} | ||
}) { | ||
@@ -951,3 +1018,4 @@ const _a = options, { port = 3e3 } = _a, restOfOptions = __objRest(_a, ["port"]); | ||
scenarios, | ||
options: restOfOptions | ||
options: restOfOptions, | ||
groups | ||
}); | ||
@@ -954,0 +1022,0 @@ return (0, import_server_with_kill.transform)( |
{ | ||
"name": "scenario-mock-server", | ||
"version": "1.1.0", | ||
"version": "1.2.0", | ||
"description": "Mock server powered by scenarios", | ||
@@ -5,0 +5,0 @@ "main": "dist/index.js", |
@@ -16,6 +16,11 @@ # Scenario Mock Server | ||
- [sms-context-id header](#sms-context-id-header) | ||
- [Additional API paths](#additional-api-paths) | ||
- [/scenarios](#scenarios) | ||
- [/select-scenario](#select-scenario) | ||
- [/groups](#groups) | ||
- [API](#api) | ||
- [createExpressApp](#createexpressapp) | ||
- [run](#run) | ||
- [scenarios](#scenarios) | ||
- [scenarios](#scenarios-1) | ||
- [groups](#groups-1) | ||
- [options](#options) | ||
@@ -125,2 +130,47 @@ - [Types](#types) | ||
## Additional API paths | ||
In addition to responding to API requests as set up by the currently active scenario a few additional endpoints exist that return json: | ||
- `/scenarios` | ||
- `/select-scenario` | ||
- `/groups` | ||
These paths can be modified by using `options` (in case they clash with paths from scnearios). | ||
### /scenarios | ||
Returns an array of scenarios available. Use `GET`. | ||
```ts | ||
type ApiScenario = { | ||
id: string; | ||
name: string; | ||
description: null | string; | ||
selected: boolean; | ||
group: null | string; | ||
}; | ||
``` | ||
### /select-scenario | ||
Allows you to select which scenario is active. Use `PUT` and the following body: | ||
```json | ||
{ | ||
"scenarioId": "{SCENARIO_YOU_WANT_TO_SELECT}" | ||
} | ||
``` | ||
### /groups | ||
Returns an array of groups. Use `GET`. | ||
```ts | ||
type ApiGroup = { | ||
id: string; | ||
name: string; | ||
}; | ||
``` | ||
## API | ||
@@ -142,3 +192,3 @@ | ||
> `{ [scenarioId]: Array<Mock> | { name, description, context, mocks, extend } }` | ||
> `{ [scenarioId]: Array<Mock> | { name, description, context, mocks, extend, group } }` | ||
@@ -156,6 +206,18 @@ <!-- https://www.tablesgenerator.com/markdown_tables --> | ||
| extend | `string` | `undefined` | Use for extending other scenarios. Requires a scenario id. | | ||
| group | `string` | `undefined` | Used for grouping scenarios in the UI. | | ||
#### groups | ||
> `{ [groupId]: groupName }` | ||
<!-- https://www.tablesgenerator.com/markdown_tables --> | ||
| Property | Type | Default | Description | | ||
| --------- | -------- | ---------- | ----------------------------------------------------- | | ||
| groupId | `string` | _required_ | Group id. Matches with `group` assigned to scenarios. | | ||
| groupName | `string` | _required_ | Used for heading in UI when groups exist. | | ||
#### options | ||
> `{ port, uiPath, selectScenarioPath, scenariosPath, cookieMode, parallelContextSize }` | defaults to `{}` | ||
> `{ port, uiPath, selectScenarioPath, scenariosPath, groupsPath, cookieMode, parallelContextSize }` | defaults to `{}` | ||
@@ -170,2 +232,3 @@ <!-- https://www.tablesgenerator.com/markdown_tables --> | ||
| scenariosPath | `string` | `/scenarios` | API path for getting scenarios. `http://localhost:{port}{scenariosPath}` | | ||
| groupsPath | `string` | `/groups` | API path for getting groups. `http://localhost:{port}{groupsPath}` | | ||
| cookieMode | `boolean` | `false` | Whether or not to store scenario selections in a cookie rather than directly in the server | | ||
@@ -172,0 +235,0 @@ | parallelContextSize | `number` | `10` | How large to make the number of contexts that can run in parallel. See [Running tests in parallel](#running-tests-in-parallel) | |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
84469
2182
327