Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 1 addition & 7 deletions .ai/skills/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,7 @@ This directory contains project-level skills for AI coding agents.

```
.ai/skills/
plugin-docs-authoring/
SKILL.md
template-blocks-architecture/
SKILL.md
release-pr-hygiene/
SKILL.md
medusa-plugin-context/
docs/
SKILL.md
```

Expand Down
1 change: 1 addition & 0 deletions .ai/skills/docs/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ Each file has a strict scope. Do not mix responsibilities between files.
| `docs/blocks.md` | Block types, DB model vs final render model, block catalog | Technical reference |
| `docs/translations.md` | i18n system, interpolation, custom translations, adding locales | Technical reference |
| `docs/admin.md` | Admin panel user guide — what you can do, typical workflow | User-facing, no endpoints |
| `docs/tests.md` | Tests | User-facing, no endpoints |
| `docs/contributing/creating-templates.md` | Step-by-step guide for contributors creating new templates | Tutorial |

## Writing Rules
Expand Down
5 changes: 5 additions & 0 deletions .changeset/bright-birds-laugh.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@codee-sh/medusa-plugin-notification-emails": patch
---

Add Medusa integration test setup and local Docker DB config for workflow testing.
20 changes: 15 additions & 5 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,20 @@ yarn prepublishOnly # build before publish (medusa plugin:build)
yarn publish-local # publish locally (npx medusa plugin:publish)
yarn publish-package # publish to npm (dotenv npm publish --access public)
yarn email:dev # preview email templates (react-email dev server)
yarn test:unit # run unit tests
yarn test:integration:http # run integration HTTP tests
yarn test:integration:modules # run integration module tests
yarn test:db:up # start test Postgres (Docker)
yarn test:db:down # stop and remove test Postgres (Docker)
```

## Shell Scripts

Daily workflow helpers in `scripts/`:

- `scripts/create-pr.sh` — create a PR (used by `yarn pr:create`).
- `scripts/prepare-release.sh` — prepare a release branch (used by `yarn prepare-release`).

## Code Style

- Prettier: 60-char print width, no semicolons, double quotes, trailing commas (es5)
Expand Down Expand Up @@ -156,6 +168,7 @@ Do not rely on `{{translations.*}}`.
- `docs/blocks.md` — block system, DB model, final catalog
- `docs/translations.md` — i18n system, interpolation
- `docs/admin.md` — admin panel user guide
- `docs/tests.md` — tests guide
- `docs/contributing/creating-templates.md` — contributor template guide
- `CONTRIBUTING.md` — branch model, PR rules, release process

Expand All @@ -165,16 +178,13 @@ Project skills are in `.ai/skills/` (symlinked to `.cursor/skills/` and `.codex/

| Skill | When to use |
|-------|-------------|
| `plugin-docs-authoring` | Writing or updating documentation |
| `template-blocks-architecture` | Changes to template/block system |
| `release-pr-hygiene` | Preparing release PRs and changesets |
| `medusa-plugin-context` | Quick domain context before any work |
| `docs` | Writing or updating documentation |

## Rules for Agents

1. Always run `yarn format` before committing.
2. Follow the branch model: feature work from `develop`, PRs to `develop`.
3. Add a changeset (`yarn changeset`) for any user-facing change.
4. Use consistent terminology: `system`, `db`, `external`, `blocks`, `mpn-builder`, `workflow`.
5. When changing docs, follow the `plugin-docs-authoring` skill.
5. When changing docs, follow the `docs` skill.
6. Do not commit `.env`, `node_modules`, `.medusa/`, or build artifacts.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ Template IDs are resolved by prefix:
- [Blocks](./docs/blocks.md)
- [Translations](./docs/translations.md)
- [Admin](./docs/admin.md)
- [Tests](./docs/tests.md)
- [Creating Templates](./docs/contributing/creating-templates.md)

## Exports
Expand Down
22 changes: 22 additions & 0 deletions docker-compose.test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
version: "3.8"

services:
postgres-test:
image: postgres:16-alpine
container_name: medusa-plugin-test-db
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: postgres
ports:
- "5432:5432"
volumes:
- medusa_plugin_test_db:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 5s
retries: 10

volumes:
medusa_plugin_test_db:
142 changes: 142 additions & 0 deletions docs/tests.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
This document explains how to run and create tests for this plugin.

## What You Can Do

- Run integration tests for workflows.
- Use a local Docker Postgres for tests.
- Add new test files that follow Medusa’s testing conventions.

## Prerequisites

- Node.js 20+
- Yarn 4
- Docker Desktop

## Local Test Database

Start the Docker Postgres used by integration tests:

```bash
yarn test:db:up
```

The container binds to `localhost:5432`. If port `5432` is already in use,
stop the local Postgres process:

```bash
sudo lsof -nP | grep ":5432" | head -n 20
```

Then stop the process reported by the command:

```bash
sudo kill <PID>
```

## Environment Variables

Integration tests load `.env.test` if present, otherwise `.env`.
Make sure these variables exist for the test database:

```env
DB_HOST=localhost
DB_USERNAME=postgres
DB_PASSWORD=postgres
```

`medusaIntegrationTestRunner` creates a temporary database for each run,
and overrides `projectConfig.databaseUrl`. The values above are used to
create and drop databases. `DATABASE_URL` is not used for tests.

The database user must have permission to create and drop databases.

Port configuration is not supported by the test runner, so tests must
connect through port `5432`.

## Run Tests

Integration HTTP tests:

```bash
yarn test:integration:http
```

Integration module tests:

```bash
yarn test:integration:modules
```

Unit tests:

```bash
yarn test:unit
```

## Cleanup

Stop and remove the test database container and volume:

```bash
yarn test:db:down
```

## Create Tests

Integration HTTP tests go in:

```text
integration-tests/http/*.spec.ts
```

Integration module tests go in:

```text
src/modules/*/__tests__/**/*.ts
```

Unit tests go in:

```text
src/**/__tests__/**/*.unit.spec.ts
```

## Example Workflow Test

```ts
import { medusaIntegrationTestRunner } from "@medusajs/test-utils"
import { slackServiceWorkflow } from "../../src/workflows/mpn-builder-services/slack-service"

medusaIntegrationTestRunner({
moduleName: "notification-emails",
medusaConfigFile: process.cwd(),
testSuite: ({ getContainer }) => {
describe("slackServiceWorkflow", () => {
it("renders a system Slack template", async () => {
const { result } = await slackServiceWorkflow(getContainer()).run({
input: {
template_id: "system_order-placed",
data: {
backendUrl: "http://localhost:9000",
order: { id: "order_123" },
},
options: {
locale: "en",
theme: {},
translations: {},
backendUrl: "http://localhost:9000",
},
},
})

expect(Array.isArray(result.blocks)).toBe(true)
})
})
},
})
```

## See Also

- [Configuration](./configuration.md)
- [Templates](./templates.md)
39 changes: 39 additions & 0 deletions integration-tests/http/slack-service.workflow.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { medusaIntegrationTestRunner } from "@medusajs/test-utils"
import { slackServiceWorkflow } from "../../src/workflows/mpn-builder-services/slack-service"

medusaIntegrationTestRunner({
moduleName: "notification-emails",
medusaConfigFile: process.cwd(),
testSuite: ({ getContainer }) => {
describe("slackServiceWorkflow", () => {
jest.setTimeout(60 * 1000)

it("renders a system Slack template", async () => {
const { result } = await slackServiceWorkflow(
getContainer()
).run({
input: {
template_id: "system_order-placed",
data: {
backendUrl: "http://localhost:9000",
order: {
id: "order_123",
name: "Test Order",
display_id: "123",
},
},
options: {
locale: "en",
theme: {},
translations: {},
backendUrl: "http://localhost:9000",
},
},
})

expect(Array.isArray(result.blocks)).toBe(true)
expect(result.blocks.length).toBeGreaterThan(0)
})
})
},
})
3 changes: 3 additions & 0 deletions integration-tests/setup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
const { MetadataStorage } = require("@mikro-orm/core")

MetadataStorage.clear()
39 changes: 39 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
const { loadEnv } = require("@medusajs/framework/utils")

loadEnv("test", process.cwd())

module.exports = {
transform: {
"^.+\\.[jt]sx?$": [
"@swc/jest",
{
jsc: {
parser: {
syntax: "typescript",
decorators: true,
tsx: true,
},
target: "es2021",
},
},
],
},
testEnvironment: "node",
moduleFileExtensions: ["js", "ts", "tsx", "jsx", "json"],
modulePathIgnorePatterns: ["dist/"],
setupFiles: ["./integration-tests/setup.js"],
}

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",
]
}
18 changes: 18 additions & 0 deletions medusa-config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { defineConfig } from "@medusajs/framework/utils"

export default defineConfig({
projectConfig: {
http: {
adminCors: "",
authCors: "",
storeCors: "",
jwtSecret: "test",
cookieSecret: "test",
},
},
modules: {
mpnBuilder: {
resolve: "./modules/mpn-builder",
},
},
})
12 changes: 10 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@
"publish-local": "npx medusa plugin:publish",
"publish-package": "dotenv npm publish --access public",
"email:dev": "email dev --dir emails-previews",
"test:unit": "TEST_TYPE=unit NODE_OPTIONS=--experimental-vm-modules jest --silent",
"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=false --runInBand --forceExit",
"test:db:up": "docker compose -f docker-compose.test.yml up -d",
"test:db:down": "docker compose -f docker-compose.test.yml down -v",
"format": "prettier --write \"src/**/*.{ts,tsx}\"",
"format:check": "prettier --check \"src/**/*.{ts,tsx}\"",
"changeset": "changeset",
Expand Down Expand Up @@ -77,11 +82,15 @@
"@react-email/preview-server": "^4.3.1",
"@react-email/render": "^1.4.0",
"@swc/core": "1.5.7",
"@swc/jest": "^0.2.36",
"@types/jest": "^29.5.14",
"@types/lodash": "^4.17.23",
"@types/node": "^20.0.0",
"@types/react": "^18.3.2",
"@types/react-dom": "^18.2.25",
"awilix": "^8.0.1",
"dotenv": "^17.2.3",
"jest": "^29.7.0",
"prettier": "^3.0.0",
"prop-types": "^15.8.1",
"react": "^18.2.0",
Expand All @@ -91,8 +100,7 @@
"ts-node": "^10.9.2",
"typescript": "^5.6.2",
"vite": "^5.2.11",
"yalc": "^1.0.0-pre.53",
"@types/lodash": "^4.17.23"
"yalc": "^1.0.0-pre.53"
},
"engines": {
"node": ">=20"
Expand Down