docs: add routing page (#9550)
- Add a new homepage to `book` project for the routing page - Move all main doc pages to be under `/v2/learn` (and added redirects + fixed links across docs) - Other: add admin components to resources dropdown + fixes to search on mobile. Closes DX-955 Preview: https://docs-v2-git-docs-router-page-medusajs.vercel.app/v2
This commit is contained in:
@@ -0,0 +1,156 @@
|
||||
import { Prerequisites, TypeList } from "docs-ui"
|
||||
|
||||
export const metadata = {
|
||||
title: `${pageNumber} Configure Instrumentation`,
|
||||
}
|
||||
|
||||
# {metadata.title}
|
||||
|
||||
In this chapter, you'll learn about observability in Medusa and how to configure instrumentation with OpenTelemetry.
|
||||
|
||||
## Observability with OpenTelemtry
|
||||
|
||||
Medusa uses [OpenTelemetry](https://opentelemetry.io/) for instrumentation and reporting. When configured, it reports traces for:
|
||||
|
||||
- HTTP requests
|
||||
- Workflow executions
|
||||
- Query usages
|
||||
- Database queries and operations
|
||||
|
||||
---
|
||||
|
||||
## How to Configure Instrumentation in Medusa?
|
||||
|
||||
<Prerequisites
|
||||
items={[
|
||||
{
|
||||
text: "An exporter to visualize your application's traces, such as Zipkin.",
|
||||
link: "https://zipkin.io/pages/quickstart.html"
|
||||
}
|
||||
]}
|
||||
/>
|
||||
|
||||
### Install Dependencies
|
||||
|
||||
Start by installing the following OpenTelemetry dependencies in your Medusa project:
|
||||
|
||||
```bash npm2yarn
|
||||
npm install @opentelemetry/sdk-node @opentelemetry/resources @opentelemetry/sdk-trace-node @opentelemetry/instrumentation-pg
|
||||
```
|
||||
|
||||
Also, install the dependencies relevant for the exporter you use. If you're using Zipkin, install the following dependencies:
|
||||
|
||||
```bash npm2yarn
|
||||
npm install @opentelemetry/exporter-zipkin
|
||||
```
|
||||
|
||||
### Add instrumentation.ts
|
||||
|
||||
Next, create the file `instrumentation.ts` with the following content:
|
||||
|
||||
```ts title="instrumentation.ts"
|
||||
import { registerOtel } from "@medusajs/medusa"
|
||||
import { ZipkinExporter } from "@opentelemetry/exporter-zipkin"
|
||||
|
||||
// If using an exporter other than Zipkin, initialize it here.
|
||||
const exporter = new ZipkinExporter({
|
||||
serviceName: "my-medusa-project",
|
||||
})
|
||||
|
||||
export function register() {
|
||||
registerOtel({
|
||||
serviceName: "medusajs",
|
||||
// pass exporter
|
||||
exporter,
|
||||
instrument: {
|
||||
http: true,
|
||||
workflows: true,
|
||||
query: true,
|
||||
},
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
In the `instrumentation.ts` file, you export a `register` function that uses Medusa's `registerOtel` utility function.
|
||||
|
||||
You also initialize an instance of the exporter, such as Zipkin, and pass it to the `registerOtel` function.
|
||||
|
||||
The `registerOtel` utility function accepts an object having the following properties:
|
||||
|
||||
<TypeList
|
||||
types={[
|
||||
{
|
||||
name: "serviceName",
|
||||
type: "`string`",
|
||||
description: "The name of the service traced.",
|
||||
optional: false
|
||||
},
|
||||
{
|
||||
name: "exporter",
|
||||
type: "[SpanExporter](https://open-telemetry.github.io/opentelemetry-js/interfaces/_opentelemetry_sdk_trace_base.SpanExporter.html)",
|
||||
description: "An instance of an exporter, such as Zipkin."
|
||||
},
|
||||
{
|
||||
name: "instrument",
|
||||
type: "`object`",
|
||||
description: "Options specifying what to trace.",
|
||||
optional: true,
|
||||
children: [
|
||||
{
|
||||
name: "http",
|
||||
type: "`boolean`",
|
||||
description: "Whether to trace HTTP requests."
|
||||
},
|
||||
{
|
||||
name: "query",
|
||||
type: "`boolean`",
|
||||
description: "Whether to trace Query usages."
|
||||
},
|
||||
{
|
||||
name: "workflows",
|
||||
type: "`boolean`",
|
||||
description: "Whether to trace Workflow executions."
|
||||
},
|
||||
{
|
||||
name: "db",
|
||||
type: "`boolean`",
|
||||
description: "Whether to trace database queries and operations."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "instrumentations",
|
||||
type: "[Instrumentation[]](https://open-telemetry.github.io/opentelemetry-js/interfaces/_opentelemetry_instrumentation.Instrumentation.html)",
|
||||
description: "Additional instrumentation options that OpenTelemetry accepts.",
|
||||
optional: true
|
||||
}
|
||||
]}
|
||||
/>
|
||||
|
||||
---
|
||||
|
||||
## Test it Out
|
||||
|
||||
To test it out, start your exporter, such as Zipkin.
|
||||
|
||||
Then, start your Medusa application:
|
||||
|
||||
```bash npm2yarn
|
||||
npm run dev
|
||||
```
|
||||
|
||||
Try to open the Medusa Admin or send a request to an API route.
|
||||
|
||||
If you check traces in your exporter, you'll find new traces reported.
|
||||
|
||||
### Trace Span Names
|
||||
|
||||
Trace span names start with the following keywords based on what it's reporting:
|
||||
|
||||
- `{methodName} {URL}` when reporting HTTP requests, where `{methodName}` is the HTTP method, and `{URL}` is the URL the request is sent to.
|
||||
- `route:` when reporting route handlers running on an HTTP requests.
|
||||
- `middleware:` when reporting a middleware running on an HTTP request.
|
||||
- `workflow:` when reporting a workflow execution.
|
||||
- `step:` when reporting a step in a workflow execution.
|
||||
- `query.graph:` when reporting Query usages.
|
||||
- `pg.query:` when reporting database queries and operations.
|
||||
@@ -0,0 +1,159 @@
|
||||
export const metadata = {
|
||||
title: `${pageNumber} Logging`,
|
||||
}
|
||||
|
||||
# {metadata.title}
|
||||
|
||||
In this chapter, you’ll learn how to use Medusa’s logging utility.
|
||||
|
||||
## Logger Class
|
||||
|
||||
Medusa provides a `Logger` class with advanced logging functionalities. This includes configuring logging levels or saving logs to a file.
|
||||
|
||||
The Medusa application registers the `Logger` class in the Medusa container and each module's container as `logger`.
|
||||
|
||||
---
|
||||
|
||||
## How to Log a Message
|
||||
|
||||
Resolve the `logger` using the Medusa container to log a message in your resource.
|
||||
|
||||
For example, create the file `src/jobs/log-message.ts` with the following content:
|
||||
|
||||
export const highlights = [
|
||||
["7", "resolve", "Resolve the `Logger` class."],
|
||||
["9", "info", "Log a message of level `info`."]
|
||||
]
|
||||
|
||||
```ts title="src/jobs/log-message.ts" highlights={highlights}
|
||||
import { MedusaContainer } from "@medusajs/framework/types"
|
||||
import { ContainerRegistrationKeys } from "@medusajs/framework/utils"
|
||||
|
||||
export default async function myCustomJob(
|
||||
container: MedusaContainer
|
||||
) {
|
||||
const logger = container.resolve(ContainerRegistrationKeys.LOGGER)
|
||||
|
||||
logger.info("I'm using the logger!")
|
||||
}
|
||||
|
||||
export const config = {
|
||||
name: "test-logger",
|
||||
// execute every minute
|
||||
schedule: "* * * * *",
|
||||
}
|
||||
```
|
||||
|
||||
This creates a scheduled job that resolves the `logger` from the Medusa container and uses it to log a message.
|
||||
|
||||
### Test the Scheduled Job
|
||||
|
||||
To test out the above scheduled job, start the Medusa application:
|
||||
|
||||
```bash npm2yarn
|
||||
npm run dev
|
||||
```
|
||||
|
||||
After a minute, you'll see the following message as part of the logged messages:
|
||||
|
||||
```text
|
||||
info: I'm using the logger!
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Log Levels
|
||||
|
||||
The `Logger` class has the following methods:
|
||||
|
||||
- `info`: The message is logged with level `info`.
|
||||
- `warn`: The message is logged with level `warn`.
|
||||
- `error`: The message is logged with level `error`.
|
||||
- `debug`: The message is logged with level `debug`.
|
||||
|
||||
Each of these methods accepts a string parameter to log in the terminal with the associated level.
|
||||
|
||||
---
|
||||
|
||||
## Logging Configurations
|
||||
|
||||
### Log Level
|
||||
|
||||
The available log levels, from lowest to highest levels, are:
|
||||
|
||||
1. `silly` (default, meaning messages of all levels are logged)
|
||||
2. `debug`
|
||||
3. `info`
|
||||
4. `warn`
|
||||
5. `error`
|
||||
|
||||
You can change that by setting the `LOG_LEVEL` environment variable to the minimum level you want to be logged.
|
||||
|
||||
For example:
|
||||
|
||||
```bash
|
||||
LOG_LEVEL=error
|
||||
```
|
||||
|
||||
This logs `error` messages only.
|
||||
|
||||
<Note title="Important">
|
||||
|
||||
The environment variable must be set as a system environment variable and not in `.env`.
|
||||
|
||||
</Note>
|
||||
|
||||
### Save Logs in a File
|
||||
|
||||
Aside from showing the logs in the terminal, you can save the logs in a file by setting the `LOG_FILE` environment variable to the path of the file relative to the Medusa server’s root directory.
|
||||
|
||||
For example:
|
||||
|
||||
```bash
|
||||
LOG_FILE=all.log
|
||||
```
|
||||
|
||||
Your logs are now saved in the `all.log` file at the root of your Medusa application.
|
||||
|
||||
<Note title="Important">
|
||||
|
||||
The environment variable must be set as a system environment variable and not in `.env`.
|
||||
|
||||
</Note>
|
||||
|
||||
---
|
||||
|
||||
## Show Log with Progress
|
||||
|
||||
The `Logger` class has an `activity` method used to log a message of level `info`. If the Medusa application is running in a development environment, a spinner starts to show the activity's progress.
|
||||
|
||||
For example:
|
||||
|
||||
```ts title="src/jobs/log-message.ts"
|
||||
import { MedusaContainer } from "@medusajs/framework/types"
|
||||
import { ContainerRegistrationKeys } from "@medusajs/framework/utils"
|
||||
|
||||
export default async function myCustomJob(
|
||||
container: MedusaContainer
|
||||
) {
|
||||
const logger = container.resolve(ContainerRegistrationKeys.LOGGER)
|
||||
|
||||
const activityId = logger.activity("First log message")
|
||||
|
||||
logger.progress(activityId, `Second log message`)
|
||||
|
||||
logger.success(activityId, "Last log message")
|
||||
}
|
||||
```
|
||||
|
||||
The `activity` method returns the ID of the started activity. This ID can then be passed to one of the following methods of the `Logger` class:
|
||||
|
||||
- `progress`: Log a message of level `info` that indicates progress within that same activity.
|
||||
- `success`: Log a message of level `info` that indicates that the activity has succeeded. This also ends the associated activity.
|
||||
- `failure`: Log a message of level `error` that indicates that the activity has failed. This also ends the associated activity.
|
||||
|
||||
<Note>
|
||||
|
||||
If you configured the `LOG_LEVEL` environment variable to a level higher than those associated with the above methods, their messages won’t be logged.
|
||||
|
||||
</Note>
|
||||
@@ -0,0 +1,12 @@
|
||||
export const metadata = {
|
||||
title: `${pageNumber} Debugging and Testing`,
|
||||
}
|
||||
|
||||
# {metadata.title}
|
||||
|
||||
In the next chapters, you’ll learn about the tools Medusa provides for testing and debugging your Medusa application.
|
||||
|
||||
By the end of this chapter, you’ll learn:
|
||||
|
||||
- How to use Medusa's `medusa-test-utils` test to write integration tests.
|
||||
- How to use Medusa’s `Logger` utility to log messages.
|
||||
+295
@@ -0,0 +1,295 @@
|
||||
import { Prerequisites } from "docs-ui"
|
||||
|
||||
export const metadata = {
|
||||
title: `${pageNumber} Example: Write Integration Tests for API Routes`,
|
||||
}
|
||||
|
||||
# {metadata.title}
|
||||
|
||||
In this chapter, you'll learn how to write integration tests for API routes using the [medusaIntegrationTestRunner utility function](../page.mdx).
|
||||
|
||||
<Prerequisites
|
||||
items={[
|
||||
{
|
||||
text: "Testing Tools Setup",
|
||||
link: "/debugging-and-testing/testing-tools"
|
||||
}
|
||||
]}
|
||||
/>
|
||||
|
||||
## Test a GET API Route
|
||||
|
||||
Consider the following API route created at `src/api/custom/route.ts`:
|
||||
|
||||
```ts title="src/api/custom/route.ts"
|
||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
||||
|
||||
export async function GET(
|
||||
req: MedusaRequest,
|
||||
res: MedusaResponse
|
||||
){
|
||||
res.json({
|
||||
message: "Hello, World!",
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
To write an integration test that tests this API route, create the file `integration-tests/http/custom-routes.spec.ts` with the following content:
|
||||
|
||||
export const getHighlights = [
|
||||
["8", "api.get", "Send a GET request to the `/custom` API route."]
|
||||
]
|
||||
|
||||
```ts title="integration-tests/http/custom-routes.spec.ts" highlights={getHighlights}
|
||||
import { medusaIntegrationTestRunner } from "medusa-test-utils"
|
||||
|
||||
medusaIntegrationTestRunner({
|
||||
testSuite: ({ api, getContainer }) => {
|
||||
describe("Custom endpoints", () => {
|
||||
describe("GET /custom", () => {
|
||||
it("returns correct message", async () => {
|
||||
const response = await api.get(
|
||||
`/custom`
|
||||
)
|
||||
|
||||
expect(response.status).toEqual(200)
|
||||
expect(response.data).toHaveProperty("message")
|
||||
expect(response.data.message).toEqual("Hello, World!")
|
||||
})
|
||||
})
|
||||
})
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
You use the `medusaIntegrationTestRunner` to write your tests.
|
||||
|
||||
You add a single test that sends a `GET` request to `/custom` using the `api.get` method. For the test to pass, the response is expected to:
|
||||
|
||||
- Have a code status `200`,
|
||||
- Have a `message` property in the returned data.
|
||||
- Have the value of the `message` property equal to `Hello, World!`.
|
||||
|
||||
### Run Tests
|
||||
|
||||
Run the following command to run your tests:
|
||||
|
||||
```bash npm2yarn
|
||||
npm run test:integration
|
||||
```
|
||||
|
||||
<Note title="Tip">
|
||||
|
||||
If you don't have a `test:integration` script in `package.json`, refer to the [Medusa Testing Tools chapter](../../page.mdx#add-test-commands).
|
||||
|
||||
</Note>
|
||||
|
||||
This runs your Medusa application and runs the tests available under the `src/integrations/http` directory.
|
||||
|
||||
---
|
||||
|
||||
## Test a POST API Route
|
||||
|
||||
Suppose you have a `hello` module whose main service extends the service factory, and that has the following model:
|
||||
|
||||
```ts title="src/modules/hello/models/my-custom.ts"
|
||||
import { model } from "@medusajs/framework/utils"
|
||||
|
||||
const MyCustom = model.define("my_custom", {
|
||||
id: model.id().primaryKey(),
|
||||
name: model.text(),
|
||||
})
|
||||
|
||||
export default MyCustom
|
||||
```
|
||||
|
||||
And consider that the file `src/api/custom/route.ts` defines another route handler for `POST` requests:
|
||||
|
||||
```ts title="src/api/custom/route.ts"
|
||||
// other imports...
|
||||
import HelloModuleService from "../../../modules/hello/service"
|
||||
|
||||
// ...
|
||||
|
||||
export async function POST(
|
||||
req: MedusaRequest,
|
||||
res: MedusaResponse
|
||||
) {
|
||||
const helloModuleService: HelloModuleService = req.scope.resolve(
|
||||
"helloModuleService"
|
||||
)
|
||||
|
||||
const myCustom = await helloModuleService.createMyCustoms(
|
||||
req.body
|
||||
)
|
||||
|
||||
res.json({
|
||||
my_custom: myCustom,
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
This API route creates a new record of `MyCustom`.
|
||||
|
||||
To write tests for this API route, add the following at the end of the `testSuite` function in `integration-tests/http/custom-routes.spec.ts`:
|
||||
|
||||
export const postHighlights = [
|
||||
["14", "api.post", "Send a POST request to the `/custom` API route."]
|
||||
]
|
||||
|
||||
```ts title="integration-tests/http/custom-routes.spec.ts" highlights={postHighlights}
|
||||
// other imports...
|
||||
import HelloModuleService from "../../src/modules/hello/service"
|
||||
|
||||
medusaIntegrationTestRunner({
|
||||
testSuite: ({ api, getContainer }) => {
|
||||
describe("Custom endpoints", () => {
|
||||
// other tests...
|
||||
|
||||
describe("POST /custom", () => {
|
||||
const id = "1"
|
||||
|
||||
it("Creates my custom", async () => {
|
||||
|
||||
const response = await api.post(
|
||||
`/custom`,
|
||||
{
|
||||
id,
|
||||
name: "Test",
|
||||
}
|
||||
)
|
||||
|
||||
expect(response.status).toEqual(200)
|
||||
expect(response.data).toHaveProperty("my_custom")
|
||||
expect(response.data.my_custom).toEqual({
|
||||
id,
|
||||
name: "Test",
|
||||
created_at: expect.any(String),
|
||||
updated_at: expect.any(String),
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
This adds a test for the `POST /custom` API route. It uses `api.post` to send the POST request. The `api.post` method accepts as a second parameter the data to pass in the request body.
|
||||
|
||||
The test passes if the response has:
|
||||
|
||||
- Status code `200`.
|
||||
- A `my_custom` property in its data.
|
||||
- Its `id` and `name` match the ones provided to the request.
|
||||
|
||||
### Tear Down Created Record
|
||||
|
||||
To ensure consistency in the database for the rest of the tests after the above test is executed, utilize [Jest's setup and teardown hooks](https://jestjs.io/docs/setup-teardown) to delete the created record.
|
||||
|
||||
Use the `getContainer` function passed as a parameter to the `testSuite` function to resolve a service and use it for setup or teardown purposes
|
||||
|
||||
So, add an `afterAll` hook in the `describe` block for `POST /custom`:
|
||||
|
||||
```ts title="integration-tests/http/custom-routes.spec.ts"
|
||||
// other imports...
|
||||
import HelloModuleService from "../../src/modules/hello/service"
|
||||
|
||||
medusaIntegrationTestRunner({
|
||||
testSuite: ({ api, getContainer }) => {
|
||||
describe("Custom endpoints", () => {
|
||||
// other tests...
|
||||
|
||||
describe("POST /custom", () => {
|
||||
// ...
|
||||
afterAll(() => async () => {
|
||||
const helloModuleService: HelloModuleService = getContainer().resolve(
|
||||
"helloModuleService"
|
||||
)
|
||||
|
||||
await helloModuleService.deleteMyCustoms(id)
|
||||
})
|
||||
})
|
||||
})
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
The `afterAll` hook resolves the `HelloModuleService` and use its `deleteMyCustoms` to delete the record created by the test.
|
||||
|
||||
---
|
||||
|
||||
## Test a DELETE API Route
|
||||
|
||||
Consider a `/custom/:id` API route created at `src/api/custom/[id]/route.ts`:
|
||||
|
||||
```ts title="src/api/custom/[id]/route.ts"
|
||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
||||
import HelloModuleService from "../../../modules/hello/service"
|
||||
|
||||
export async function DELETE(
|
||||
req: MedusaRequest,
|
||||
res: MedusaResponse
|
||||
) {
|
||||
const helloModuleService: HelloModuleService = req.scope.resolve(
|
||||
"helloModuleService"
|
||||
)
|
||||
|
||||
await helloModuleService.deleteMyCustoms(req.params.id)
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
This API route accepts an ID path parameter, and uses the `HelloModuleService` to delete a `MyCustom` record by that ID.
|
||||
|
||||
To add tests for this API route, add the following to `integration-tests/http/custom-routes.spec.ts`:
|
||||
|
||||
export const deleteHighlights = [
|
||||
["21", "api.delete", "Send a DELETE request to the `/custom/:id` API route."]
|
||||
]
|
||||
|
||||
```ts title="integration-tests/http/custom-routes.spec.ts" highlights={deleteHighlights}
|
||||
medusaIntegrationTestRunner({
|
||||
testSuite: ({ api, getContainer }) => {
|
||||
describe("Custom endpoints", () => {
|
||||
// ...
|
||||
|
||||
describe("DELETE /custom/:id", () => {
|
||||
const id = "1"
|
||||
|
||||
beforeAll(() => async () => {
|
||||
const helloModuleService: HelloModuleService = getContainer().resolve(
|
||||
"helloModuleService"
|
||||
)
|
||||
|
||||
await helloModuleService.createMyCustoms({
|
||||
id,
|
||||
name: "Test",
|
||||
})
|
||||
})
|
||||
|
||||
it("Deletes my custom", async () => {
|
||||
const response = await api.delete(
|
||||
`/custom/${id}`
|
||||
)
|
||||
|
||||
expect(response.status).toEqual(200)
|
||||
expect(response.data).toHaveProperty("success")
|
||||
expect(response.data.success).toBeTruthy()
|
||||
})
|
||||
})
|
||||
})
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
This adds a new test for the `DELETE /custom/:id` API route. You use the `beforeAll` hook to create a `MyCustom` record using the `HelloModuleService`.
|
||||
|
||||
In the test, you use the `api.delete` method to send a `DELETE` request to `/custom/:id`. The test passes if the response:
|
||||
|
||||
- Has a `200` status code.
|
||||
- Has a `success` property in its data.
|
||||
- The `success` property's value is true.
|
||||
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
import { Prerequisites } from "docs-ui"
|
||||
|
||||
export const metadata = {
|
||||
title: `${pageNumber} Write Integration Tests`,
|
||||
}
|
||||
|
||||
# {metadata.title}
|
||||
|
||||
In this chapter, you'll learn about the `medusaIntegrationTestRunner` utility function used to write integration tests.
|
||||
|
||||
<Prerequisites
|
||||
items={[
|
||||
{
|
||||
text: "Testing Tools Setup",
|
||||
link: "/debugging-and-testing/testing-tools"
|
||||
}
|
||||
]}
|
||||
/>
|
||||
|
||||
## medusaIntegrationTestRunner Utility
|
||||
|
||||
The `medusaIntegrationTestRunner` utility function is provided by the `medusa-test-utils` package to create integration tests in your Medusa project. It runs a full Medusa application, allowing you test API routes, workflows, or other customizations.
|
||||
|
||||
For example:
|
||||
|
||||
export const highlights = [
|
||||
["4", "api", "A set of utility methods used to send requests to the Medusa application."],
|
||||
["4", "getContainer", "A function to retrieve the Medusa container."]
|
||||
]
|
||||
|
||||
```ts title="integration-tests/http/test.spec.ts" highlights={highlights}
|
||||
import { medusaIntegrationTestRunner } from "medusa-test-utils"
|
||||
|
||||
medusaIntegrationTestRunner({
|
||||
testSuite: ({ api, getContainer }) => {
|
||||
// TODO write tests...
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
The `medusaIntegrationTestRunner` function accepts an object as a parameter. The object has a required property `testSuite`.
|
||||
|
||||
`testSuite`'s value is a function that defines the tests to run. The function accepts as a parameter an object that has the following properties:
|
||||
|
||||
- `api`: a set of utility methods used to send requests to the Medusa application. It has the following methods:
|
||||
- `get`: Send a `GET` request to an API route.
|
||||
- `post`: Send a `POST` request to an API route.
|
||||
- `delete`: Send a `DELETE` request to an API route.
|
||||
- `getContainer`: a function that retrieves the Medusa Container. Use the `getContainer().resolve` method to resolve resources from the Medusa Container.
|
||||
|
||||
The tests in the `testSuite` function are written using [Jest](https://jestjs.io/).
|
||||
|
||||
---
|
||||
|
||||
### Run Tests
|
||||
|
||||
Run the following command to run your tests:
|
||||
|
||||
```bash npm2yarn
|
||||
npm run test:integration
|
||||
```
|
||||
|
||||
<Note title="Tip">
|
||||
|
||||
If you don't have a `test:integration` script in `package.json`, refer to the [Medusa Testing Tools chapter](../page.mdx#add-test-commands).
|
||||
|
||||
</Note>
|
||||
|
||||
This runs your Medusa application and runs the tests available under the `src/integrations/http` directory.
|
||||
|
||||
---
|
||||
|
||||
## Other Options and Inputs
|
||||
|
||||
Refer to [this reference in the Development Resources documentation](!resources!/test-tools-reference/medusaIntegrationTestRunner) for other available parameter options and inputs of the `testSuite` function.
|
||||
|
||||
---
|
||||
|
||||
## Database Used in Tests
|
||||
|
||||
The `medusaIntegrationTestRunner` function creates a database with a random name before running the tests. Then, it drops that database after all the tests end.
|
||||
|
||||
To manage that database, such as changing its name or perform operations on it in your tests, refer to the [references in the Development Resources documentation](!resources!/test-tools-reference/medusaIntegrationTestRunner).
|
||||
|
||||
---
|
||||
|
||||
## Example Integration Tests
|
||||
|
||||
The next chapters provide examples of writing integration tests for API routes and workflows.
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
import { Prerequisites } from "docs-ui"
|
||||
|
||||
export const metadata = {
|
||||
title: `${pageNumber} Example: Write Integration Tests for Workflows`,
|
||||
}
|
||||
|
||||
# {metadata.title}
|
||||
|
||||
In this chapter, you'll learn how to write integration tests for workflows using the [medusaIntegrationTestRunner utility function](../page.mdx).
|
||||
|
||||
<Prerequisites
|
||||
items={[
|
||||
{
|
||||
text: "Testing Tools Setup",
|
||||
link: "/debugging-and-testing/testing-tools"
|
||||
}
|
||||
]}
|
||||
/>
|
||||
|
||||
## Write Integration Test for Workflow
|
||||
|
||||
Consider you have the following workflow defined at `src/workflows/hello-world.ts`:
|
||||
|
||||
```ts title="src/workflows/hello-world.ts"
|
||||
import {
|
||||
createWorkflow,
|
||||
createStep,
|
||||
StepResponse,
|
||||
WorkflowResponse,
|
||||
} from "@medusajs/framework/workflows-sdk"
|
||||
|
||||
const step1 = createStep("step-1", () => {
|
||||
return new StepResponse("Hello, World!")
|
||||
})
|
||||
|
||||
export const helloWorldWorkflow = createWorkflow(
|
||||
"hello-world-workflow",
|
||||
() => {
|
||||
const message = step1()
|
||||
|
||||
return new WorkflowResponse(message)
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
To write a test for this workflow, create the file `integration-tests/http/workflow.spec.ts` with the following content:
|
||||
|
||||
```ts title="integration-tests/http/workflow.spec.ts"
|
||||
import { medusaIntegrationTestRunner } from "medusa-test-utils"
|
||||
import { helloWorldWorkflow } from "../../src/workflows/hello-world"
|
||||
|
||||
medusaIntegrationTestRunner({
|
||||
testSuite: ({ getContainer }) => {
|
||||
describe("Test hello-world workflow", () => {
|
||||
it("returns message", async () => {
|
||||
const { result } = await helloWorldWorkflow(getContainer())
|
||||
.run()
|
||||
|
||||
expect(result).toEqual("Hello, World!")
|
||||
})
|
||||
})
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
You use the `medusaIntegrationTestRunner` to write an integration test for the workflow. The test pases if the workflow returns the string `"Hello, World!"`.
|
||||
|
||||
---
|
||||
|
||||
## Run Test
|
||||
|
||||
Run the following command to run your tests:
|
||||
|
||||
```bash npm2yarn
|
||||
npm run test:integration
|
||||
```
|
||||
|
||||
<Note title="Tip">
|
||||
|
||||
If you don't have a `test:integration` script in `package.json`, refer to the [Medusa Testing Tools chapter](../../page.mdx#add-test-commands).
|
||||
|
||||
</Note>
|
||||
|
||||
This runs your Medusa application and runs the tests available under the `integrations/http` directory.
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
import { Prerequisites } from "docs-ui"
|
||||
|
||||
export const metadata = {
|
||||
title: `${pageNumber} Example: Integration Tests for a Module`,
|
||||
}
|
||||
|
||||
# {metadata.title}
|
||||
|
||||
In this chapter, find an example of writing an integration test for a module using the [moduleIntegrationTestRunner utility function](../page.mdx).
|
||||
|
||||
<Prerequisites
|
||||
items={[
|
||||
{
|
||||
text: "Testing Tools Setup",
|
||||
link: "/debugging-and-testing/testing-tools"
|
||||
}
|
||||
]}
|
||||
/>
|
||||
|
||||
## Write Integration Test for Module
|
||||
|
||||
Consider a `hello` module with a `HelloModuleService` that has a `getMessage` method:
|
||||
|
||||
```ts title="src/modules/hello/service.ts"
|
||||
import { MedusaService } from "@medusajs/framework/utils"
|
||||
import MyCustom from "./models/my-custom"
|
||||
|
||||
class HelloModuleService extends MedusaService({
|
||||
MyCustom,
|
||||
}){
|
||||
getMessage(): string {
|
||||
return "Hello, World!"
|
||||
}
|
||||
}
|
||||
|
||||
export default HelloModuleService
|
||||
```
|
||||
|
||||
To create an integration test for the method, create the file `src/modules/hello/__tests__/service.spec.ts` with the following content:
|
||||
|
||||
```ts title="src/modules/hello/__tests__/service.spec.ts"
|
||||
import { moduleIntegrationTestRunner } from "medusa-test-utils"
|
||||
import { HELLO_MODULE } from ".."
|
||||
import HelloModuleService from "../service"
|
||||
import MyCustom from "../models/my-custom"
|
||||
|
||||
moduleIntegrationTestRunner<HelloModuleService>({
|
||||
moduleName: HELLO_MODULE,
|
||||
moduleModels: [MyCustom],
|
||||
resolve: "./src/modules/hello",
|
||||
testSuite: ({ service }) => {
|
||||
describe("HelloModuleService", () => {
|
||||
it("says hello world", () => {
|
||||
const message = service.getMessage()
|
||||
|
||||
expect(message).toEqual("Hello, World!")
|
||||
})
|
||||
})
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
You use the `moduleIntegrationTestRunner` function to add tests for the `hello` module. You have one test that passes if the `getMessage` method returns the `"Hello, World!"` string.
|
||||
|
||||
---
|
||||
|
||||
## Run Test
|
||||
|
||||
Run the following command to run your module integration tests:
|
||||
|
||||
```bash npm2yarn
|
||||
npm run test:modules
|
||||
```
|
||||
|
||||
<Note title="Tip">
|
||||
|
||||
If you don't have a `test:modules` script in `package.json`, refer to the [Medusa Testing Tools chapter](../../page.mdx#add-test-commands).
|
||||
|
||||
</Note>
|
||||
|
||||
This runs your Medusa application and runs the tests available in any `__tests__` directory under the `src/modules` directory.
|
||||
@@ -0,0 +1,132 @@
|
||||
import { Prerequisites } from "docs-ui"
|
||||
|
||||
export const metadata = {
|
||||
title: `${pageNumber} Write Tests for Modules`,
|
||||
}
|
||||
|
||||
# {metadata.title}
|
||||
|
||||
In this chapter, you'll learn about the `moduleIntegrationTestRunner` utility function and how to use it to write integration tests for a module's main service.
|
||||
|
||||
<Prerequisites
|
||||
items={[
|
||||
{
|
||||
text: "Testing Tools Setup",
|
||||
link: "/debugging-and-testing/testing-tools"
|
||||
}
|
||||
]}
|
||||
/>
|
||||
|
||||
## moduleIntegrationTestRunner Utility
|
||||
|
||||
The `moduleIntegrationTestRunner` utility function is provided by the `medusa-test-utils` package to create integration tests for a module. The integration tests run on a test Medusa application with only the specified module enabled.
|
||||
|
||||
For example, assuming you have a `hello` module, create a test file at `src/modules/hello/__tests__/service.spec.ts`:
|
||||
|
||||
```ts title="src/modules/hello/__tests__/service.spec.ts"
|
||||
import { moduleIntegrationTestRunner } from "medusa-test-utils"
|
||||
import { HELLO_MODULE } from ".."
|
||||
import HelloModuleService from "../service"
|
||||
import MyCustom from "../models/my-custom"
|
||||
|
||||
moduleIntegrationTestRunner<HelloModuleService>({
|
||||
moduleName: HELLO_MODULE,
|
||||
moduleModels: [MyCustom],
|
||||
resolve: "./src/modules/hello",
|
||||
testSuite: ({ service }) => {
|
||||
// TODO write tests
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
The `moduleIntegrationTestRunner` function accepts as a parameter an object with the following properties:
|
||||
|
||||
- `moduleName`: The name of the module.
|
||||
- `moduleModels`: An array of models in the module. Refer to [this section](#write-tests-for-modules-without-data-models) if your module doesn't have data models.
|
||||
- `resolve`: The path to the model.
|
||||
- `testSuite`: A function that defines the tests to run.
|
||||
|
||||
The `testSuite` function accepts as a parameter an object having the `service` property, which is an instance of the module's main service.
|
||||
|
||||
<Note title="Tip">
|
||||
|
||||
The type argument provided to the `moduleIntegrationTestRunner` function is used as the type of the `service` property.
|
||||
|
||||
</Note>
|
||||
|
||||
The tests in the `testSuite` function are written using [Jest](https://jestjs.io/).
|
||||
|
||||
---
|
||||
|
||||
## Run Tests
|
||||
|
||||
Run the following command to run your module integration tests:
|
||||
|
||||
```bash npm2yarn
|
||||
npm run test:modules
|
||||
```
|
||||
|
||||
<Note title="Tip">
|
||||
|
||||
If you don't have a `test:modules` script in `package.json`, refer to the [Medusa Testing Tools chapter](../page.mdx#add-test-commands).
|
||||
|
||||
</Note>
|
||||
|
||||
This runs your Medusa application and runs the tests available in any `__tests__` directory under the `src/modules` directory.
|
||||
|
||||
---
|
||||
|
||||
## Pass Module Options
|
||||
|
||||
If your module accepts options, you can set them using the `moduleOptions` property of the `moduleIntegrationTestRunner`'s parameter.
|
||||
|
||||
For example:
|
||||
|
||||
```ts
|
||||
import { moduleIntegrationTestRunner } from "medusa-test-utils"
|
||||
import HelloModuleService from "../service"
|
||||
|
||||
moduleIntegrationTestRunner<HelloModuleService>({
|
||||
moduleOptions: {
|
||||
apiKey: "123",
|
||||
},
|
||||
// ...
|
||||
})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Write Tests for Modules without Data Models
|
||||
|
||||
If your module doesn't have a data model, pass a dummy model in the `moduleModels` property.
|
||||
|
||||
For example:
|
||||
|
||||
```ts
|
||||
import { moduleIntegrationTestRunner } from "medusa-test-utils"
|
||||
import HelloModuleService from "../service"
|
||||
import { model } from "@medusajs/framework/utils"
|
||||
|
||||
const DummyModel = model.define("dummy_model", {
|
||||
id: model.id().primaryKey(),
|
||||
})
|
||||
|
||||
moduleIntegrationTestRunner<HelloModuleService>({
|
||||
moduleModels: [DummyModel],
|
||||
// ...
|
||||
})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Other Options and Inputs
|
||||
|
||||
Refer to [this reference in the Development Resources documentation](!resources!/test-tools-reference/moduleIntegrationTestRunner) for other available parameter options and inputs of the `testSuite` function.
|
||||
|
||||
---
|
||||
|
||||
## Database Used in Tests
|
||||
|
||||
The `moduleIntegrationTestRunner` function creates a database with a random name before running the tests. Then, it drops that database after all the tests end.
|
||||
|
||||
To manage that database, such as changing its name or perform operations on it in your tests, refer to the [references in the Development Resources documentation](!resources!/test-tools-reference/moduleIntegrationTestRunner).
|
||||
@@ -0,0 +1,103 @@
|
||||
export const metadata = {
|
||||
title: `${pageNumber} Medusa Testing Tools`,
|
||||
}
|
||||
|
||||
# {metadata.title}
|
||||
|
||||
In this chapter, you'll learn about Medusa's testing tools and how to install and configure them.
|
||||
|
||||
## medusa-test-utils Package
|
||||
|
||||
Medusa provides a `medusa-test-utils` package with utility tools to create integration tests for your custom API routes, modules, or other Medusa customizations.
|
||||
|
||||
### Install medusa-test-utils
|
||||
|
||||
To use the `medusa-test-utils` package, install it as a `devDependency`:
|
||||
|
||||
```bash npm2yarn
|
||||
npm install --save-dev medusa-test-utils@rc
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Install and Configure Jest
|
||||
|
||||
Writing tests with `medusa-test-utils`'s tools requires installing and configuring Jest in your project.
|
||||
|
||||
{/* TODO remove this note at some point in the future */}
|
||||
|
||||
<Note>
|
||||
|
||||
If your Medusa project was created after September 3rd, Jest is already installed and configured.
|
||||
|
||||
</Note>
|
||||
|
||||
Run the following command to install the required Jest dependencies:
|
||||
|
||||
```bash npm2yarn
|
||||
npm install --save-dev jest @types/jest @swc/jest
|
||||
```
|
||||
|
||||
Then, create the file `jest.config.js` with the following content:
|
||||
|
||||
```js title="jest.config.js"
|
||||
const { loadEnv } = require("@medusajs/framework/utils")
|
||||
loadEnv("test", process.cwd())
|
||||
|
||||
module.exports = {
|
||||
transform: {
|
||||
"^.+\\.[jt]s$": [
|
||||
"@swc/jest",
|
||||
{
|
||||
jsc: {
|
||||
parser: { syntax: "typescript", decorators: true },
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
testEnvironment: "node",
|
||||
moduleFileExtensions: ["js", "ts", "json"],
|
||||
modulePathIgnorePatterns: ["dist/"],
|
||||
}
|
||||
|
||||
if (process.env.TEST_TYPE === "integration:http") {
|
||||
module.exports.testMatch = ["**/integration-tests/http/*.spec.[jt]s"]
|
||||
} else if (process.env.TEST_TYPE === "integration:modules") {
|
||||
module.exports.testMatch = ["**/src/modules/*/__tests__/**/*.[jt]s"]
|
||||
} else if (process.env.TEST_TYPE === "unit") {
|
||||
module.exports.testMatch = ["**/src/**/__tests__/**/*.unit.spec.[jt]s"]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Add Test Commands
|
||||
|
||||
Finally, add the following scripts to `package.json`:
|
||||
|
||||
```json title="package.json"
|
||||
"scripts": {
|
||||
// ...
|
||||
"test:integration:http": "TEST_TYPE=integration:http NODE_OPTIONS=--experimental-vm-modules jest --silent=false --runInBand --forceExit",
|
||||
"test:integration:modules": "TEST_TYPE=integration:modules NODE_OPTIONS=--experimental-vm-modules jest --silent --runInBand --forceExit",
|
||||
"test:unit": "TEST_TYPE=unit NODE_OPTIONS=--experimental-vm-modules jest --silent --runInBand --forceExit"
|
||||
},
|
||||
```
|
||||
|
||||
You now have two commands:
|
||||
|
||||
- `test:integration:http` to run integration tests (for example, for API routes and workflows) available under the `integration-tests/http` directory.
|
||||
- `test:integration:modules` to run integration tests for modules available in any `__tests__` directory under `src/modules`.
|
||||
- `test:unit` to run unit tests in any `__tests__` directory under the `src` directory.
|
||||
|
||||
<Note>
|
||||
|
||||
Medusa provides utility tools for integration tests only. You can write unit tests using Jest.
|
||||
|
||||
</Note>
|
||||
|
||||
---
|
||||
|
||||
## Test Tools and Writing Tests
|
||||
|
||||
The next chapters explain how to use the testing tools provided by `medusa-test-utils` to write tests.
|
||||
Reference in New Issue
Block a user