Skip to content

Commit 07d6e48

Browse files
committed
test: set up BDD/E2E test support
1 parent e759945 commit 07d6e48

File tree

9 files changed

+1141
-17
lines changed

9 files changed

+1141
-17
lines changed

cucumber.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
module.exports = {
2+
default: {
3+
requireModule: ["ts-node/register/transpile-only"],
4+
require: [
5+
"tests/bdd/support/world.ts",
6+
"tests/bdd/support/hooks.ts",
7+
"tests/bdd/steps/**/*.ts",
8+
],
9+
paths: ["tests/bdd/features/**/*.feature"],
10+
publishQuiet: true,
11+
format: ["progress"],
12+
},
13+
};

package.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
"lint": "biome check",
1313
"format": "biome format --write",
1414
"test": "vitest",
15+
"test:bdd": "cucumber-js",
1516
"type-check": "tsc --noEmit",
1617
"prepare": "husky",
1718
"oidc": "node dev-auth/oidc-provider.mjs",
@@ -43,9 +44,12 @@
4344
},
4445
"devDependencies": {
4546
"@biomejs/biome": "2.3.6",
47+
"@cucumber/cucumber": "^12.2.0",
48+
"@cucumber/messages": "^31.0.0",
4649
"@hey-api/client-next": "0.5.1",
4750
"@hey-api/openapi-ts": "0.87.5",
4851
"@mswjs/http-middleware": "^0.10.2",
52+
"@playwright/test": "^1.56.1",
4953
"@tailwindcss/postcss": "^4",
5054
"@testing-library/dom": "^10.4.1",
5155
"@testing-library/jest-dom": "^6.9.1",
@@ -63,6 +67,7 @@
6367
"lint-staged": "^16.0.0",
6468
"oidc-provider": "^9.5.2",
6569
"tailwindcss": "^4",
70+
"ts-node": "^10.9.2",
6671
"tsx": "4.20.6",
6772
"typescript": "^5",
6873
"vite": "^7.2.2",

pnpm-lock.yaml

Lines changed: 1025 additions & 16 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/bdd/features/login.feature

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
Feature: Login flow
2+
3+
Scenario: Sign in and land on Catalog
4+
Given I am on "/signin"
5+
When I click the "Okta" button
6+
Then I should be on "/catalog"
7+
And I should see a heading "MCP Server Catalog"
8+

tests/bdd/steps/global.steps.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { Given, Then, When } from "@cucumber/cucumber";
2+
import { expect } from "@playwright/test";
3+
import type { PlaywrightWorld } from "../support/world";
4+
5+
Given("I am on {string}", async function (this: PlaywrightWorld, path: string) {
6+
await this.page.goto(`${this.baseUrl}${path}`);
7+
});
8+
9+
When(
10+
"I click the {string} button",
11+
async function (this: PlaywrightWorld, label: string) {
12+
await this.page.getByRole("button", { name: label }).click();
13+
},
14+
);
15+
16+
Then(
17+
"I should see the text {string}",
18+
async function (this: PlaywrightWorld, text: string) {
19+
await expect(this.page.getByText(text)).toBeVisible();
20+
},
21+
);
22+
23+
Then(
24+
"I should see a heading {string}",
25+
async function (this: PlaywrightWorld, heading: string) {
26+
await expect(
27+
this.page.getByRole("heading", { name: heading }),
28+
).toBeVisible();
29+
},
30+
);
31+
32+
Then(
33+
"I should be on {string}",
34+
async function (this: PlaywrightWorld, path: string) {
35+
await expect(this.page).toHaveURL(
36+
new RegExp(
37+
`${this.baseUrl}${path.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&")}$`,
38+
),
39+
);
40+
},
41+
);

tests/bdd/support/hooks.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { After, Before, setDefaultTimeout } from "@cucumber/cucumber";
2+
import {
3+
type Browser,
4+
type BrowserContext,
5+
chromium,
6+
type Page,
7+
} from "@playwright/test";
8+
import type { PlaywrightWorld } from "./world";
9+
10+
let browser: Browser | undefined;
11+
12+
setDefaultTimeout(60 * 1000); // 60s per step
13+
14+
Before(async function (this: PlaywrightWorld) {
15+
if (!browser) {
16+
browser = await chromium.launch({ headless: true });
17+
}
18+
const context: BrowserContext = await browser.newContext();
19+
const page: Page = await context.newPage();
20+
this.context = context;
21+
this.page = page;
22+
});
23+
24+
After(async function (this: PlaywrightWorld) {
25+
if (this.page) await this.page.close();
26+
if (this.context) await this.context.close();
27+
});

tests/bdd/support/world.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { setWorldConstructor, type World } from "@cucumber/cucumber";
2+
import type { BrowserContext, Page } from "@playwright/test";
3+
4+
export class PlaywrightWorld implements World {
5+
page!: Page;
6+
context!: BrowserContext;
7+
baseUrl: string;
8+
9+
constructor() {
10+
this.baseUrl = process.env.BASE_URL || "http://localhost:3000";
11+
}
12+
}
13+
14+
setWorldConstructor(PlaywrightWorld);

tests/bdd/ts-node.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// Local ts-node registration for Cucumber with CommonJS output
2+
require("ts-node").register({
3+
transpileOnly: true,
4+
compilerOptions: {
5+
module: "commonjs",
6+
},
7+
});

tsconfig.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,5 +31,5 @@
3131
"**/*.mts",
3232
".next/dev/types/**/*.ts"
3333
],
34-
"exclude": ["node_modules", ".next/dev/types/**"]
34+
"exclude": ["node_modules", ".next/dev/types/**", "tests/bdd/**"]
3535
}

0 commit comments

Comments
 (0)