mirror of
https://github.com/profullstack/logicsrc.git
synced 2026-08-13 22:37:29 +00:00
Add LogicSRC web contract and E2E tests
This commit is contained in:
parent
17942eef57
commit
8e6124d23a
6 changed files with 157 additions and 3 deletions
75
apps/logicsrc-web/contract/logicsrc-web.contract.test.ts
Normal file
75
apps/logicsrc-web/contract/logicsrc-web.contract.test.ts
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
import { spawn, type ChildProcessWithoutNullStreams } from "node:child_process";
|
||||||
|
import { accessSync } from "node:fs";
|
||||||
|
import { setTimeout as delay } from "node:timers/promises";
|
||||||
|
import { afterAll, beforeAll, describe, expect, it } from "vitest";
|
||||||
|
|
||||||
|
let server: ChildProcessWithoutNullStreams;
|
||||||
|
const port = 4291;
|
||||||
|
const baseUrl = `http://127.0.0.1:${port}`;
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
accessSync(new URL("../dist/index.html", import.meta.url));
|
||||||
|
server = spawn(process.execPath, ["server.js"], {
|
||||||
|
cwd: new URL("..", import.meta.url),
|
||||||
|
env: { ...process.env, PORT: String(port) }
|
||||||
|
});
|
||||||
|
|
||||||
|
await waitForServer();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
server?.kill();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("LogicSRC web contracts", () => {
|
||||||
|
it("serves SPA routes from the built app shell", async () => {
|
||||||
|
for (const route of ["/", "/openspec", "/docs", "/blog", "/about", "/terms", "/privacy"]) {
|
||||||
|
const response = await fetch(`${baseUrl}${route}`);
|
||||||
|
const text = await response.text();
|
||||||
|
|
||||||
|
expect(response.status, route).toBe(200);
|
||||||
|
expect(response.headers.get("content-type")).toContain("text/html");
|
||||||
|
expect(text).toContain('<div id="app"></div>');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it("serves sitemap.xml as XML with canonical routes", async () => {
|
||||||
|
const response = await fetch(`${baseUrl}/sitemap.xml`);
|
||||||
|
const text = await response.text();
|
||||||
|
|
||||||
|
expect(response.status).toBe(200);
|
||||||
|
expect(response.headers.get("content-type")).toContain("application/xml");
|
||||||
|
expect(response.headers.get("cache-control")).toBe("no-store");
|
||||||
|
expect(text).toContain("<loc>https://logicsrc.com/openspec</loc>");
|
||||||
|
expect(text).toContain("<loc>https://logicsrc.com/blog</loc>");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("serves blog/rss.xml as RSS XML", async () => {
|
||||||
|
const response = await fetch(`${baseUrl}/blog/rss.xml`);
|
||||||
|
const text = await response.text();
|
||||||
|
|
||||||
|
expect(response.status).toBe(200);
|
||||||
|
expect(response.headers.get("content-type")).toContain("application/xml");
|
||||||
|
expect(response.headers.get("cache-control")).toBe("no-store");
|
||||||
|
expect(text).toContain("<rss version=\"2.0\"");
|
||||||
|
expect(text).toContain("<title>LogicSRC OpenSpec Compatibility</title>");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
async function waitForServer() {
|
||||||
|
let lastError: unknown;
|
||||||
|
for (let attempt = 0; attempt < 40; attempt += 1) {
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${baseUrl}/health`);
|
||||||
|
if (response.ok) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
lastError = error;
|
||||||
|
}
|
||||||
|
|
||||||
|
await delay(250);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error(`LogicSRC web server did not start: ${String(lastError)}`);
|
||||||
|
}
|
||||||
37
apps/logicsrc-web/e2e/logicsrc.spec.ts
Normal file
37
apps/logicsrc-web/e2e/logicsrc.spec.ts
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
import { expect, test } from "@playwright/test";
|
||||||
|
|
||||||
|
test.describe("LogicSRC PWA", () => {
|
||||||
|
test("renders OpenSpec comparison and compatibility commands", async ({ page }) => {
|
||||||
|
await page.goto("/openspec");
|
||||||
|
|
||||||
|
await expect(page.getByRole("heading", { name: "LogicSRC", exact: true })).toBeVisible();
|
||||||
|
await expect(page.getByRole("heading", { name: "LogicSRC vs OpenSpec.dev" })).toBeVisible();
|
||||||
|
await expect(page.getByText("OpenSpec.dev currently positions itself as no-MCP.")).toBeVisible();
|
||||||
|
await expect(page.getByText("logicsrc --openspec agentswarm --yolo")).toBeVisible();
|
||||||
|
await expect(page.getByText("openspec import")).toBeVisible();
|
||||||
|
await expect(page.getByText("openspec export")).toBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("renders top-level docs and legal route targets", async ({ page }) => {
|
||||||
|
await page.goto("/privacy");
|
||||||
|
|
||||||
|
await expect(page.getByRole("heading", { name: "Top-Level Pages" })).toBeVisible();
|
||||||
|
await expect(page.getByText("/docs · Docs")).toBeVisible();
|
||||||
|
await expect(page.getByText("/blog · Blog")).toBeVisible();
|
||||||
|
await expect(page.getByText("/terms · Terms")).toBeVisible();
|
||||||
|
await expect(page.getByText("/privacy · Privacy")).toBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("serves sitemap and RSS XML endpoints", async ({ request }) => {
|
||||||
|
const sitemap = await request.get("/sitemap.xml");
|
||||||
|
const rss = await request.get("/blog/rss.xml");
|
||||||
|
|
||||||
|
expect(sitemap.status()).toBe(200);
|
||||||
|
expect(sitemap.headers()["content-type"]).toMatch(/(?:application|text)\/xml/);
|
||||||
|
await expect(sitemap.text()).resolves.toContain("https://logicsrc.com/openspec");
|
||||||
|
|
||||||
|
expect(rss.status()).toBe(200);
|
||||||
|
expect(rss.headers()["content-type"]).toMatch(/(?:application|text)\/xml/);
|
||||||
|
await expect(rss.text()).resolves.toContain("LogicSRC OpenSpec Compatibility");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -6,11 +6,18 @@
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "node scripts/check-assets.js && vite build",
|
"build": "node scripts/check-assets.js && vite build",
|
||||||
"dev": "vite --host 0.0.0.0",
|
"dev": "vite --host 0.0.0.0",
|
||||||
"start": "node server.js"
|
"start": "node server.js",
|
||||||
|
"test": "vitest run contract",
|
||||||
|
"test:contract": "vitest run contract",
|
||||||
|
"test:e2e": "playwright test"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@logicsrc/commandboard-api": "file:../commandboard-api",
|
"@logicsrc/commandboard-api": "file:../commandboard-api",
|
||||||
"typescript": "^5.9.3",
|
"typescript": "^5.9.3",
|
||||||
"vite": "^7.2.6"
|
"vite": "^7.2.6"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@playwright/test": "^1.57.0",
|
||||||
|
"vitest": "^4.0.8"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
31
apps/logicsrc-web/playwright.config.ts
Normal file
31
apps/logicsrc-web/playwright.config.ts
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
import { defineConfig, devices } from "@playwright/test";
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
testDir: "./e2e",
|
||||||
|
timeout: 30_000,
|
||||||
|
expect: {
|
||||||
|
timeout: 5_000
|
||||||
|
},
|
||||||
|
fullyParallel: true,
|
||||||
|
reporter: process.env.CI ? [["github"], ["list"]] : "list",
|
||||||
|
use: {
|
||||||
|
baseURL: "http://127.0.0.1:5174",
|
||||||
|
trace: "on-first-retry"
|
||||||
|
},
|
||||||
|
projects: [
|
||||||
|
{
|
||||||
|
name: "chromium",
|
||||||
|
use: { ...devices["Desktop Chrome"] }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "mobile-chrome",
|
||||||
|
use: { ...devices["Pixel 7"] }
|
||||||
|
}
|
||||||
|
],
|
||||||
|
webServer: {
|
||||||
|
command: "npm run dev -- --port 5174",
|
||||||
|
url: "http://127.0.0.1:5174",
|
||||||
|
reuseExistingServer: !process.env.CI,
|
||||||
|
timeout: 30_000
|
||||||
|
}
|
||||||
|
});
|
||||||
4
package-lock.json
generated
4
package-lock.json
generated
|
|
@ -128,6 +128,10 @@
|
||||||
"@logicsrc/commandboard-api": "file:../commandboard-api",
|
"@logicsrc/commandboard-api": "file:../commandboard-api",
|
||||||
"typescript": "^5.9.3",
|
"typescript": "^5.9.3",
|
||||||
"vite": "^7.2.6"
|
"vite": "^7.2.6"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@playwright/test": "^1.57.0",
|
||||||
|
"vitest": "^4.0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"apps/logicsrc-web/node_modules/vite": {
|
"apps/logicsrc-web/node_modules/vite": {
|
||||||
|
|
|
||||||
|
|
@ -17,8 +17,8 @@
|
||||||
"test": "npm run test --workspaces --if-present",
|
"test": "npm run test --workspaces --if-present",
|
||||||
"check": "npm run build && npm run test",
|
"check": "npm run build && npm run test",
|
||||||
"schemas:validate": "npm --workspace @logicsrc/validators run validate:fixtures",
|
"schemas:validate": "npm --workspace @logicsrc/validators run validate:fixtures",
|
||||||
"test:contract": "npm --workspace @logicsrc/commandboard-api run test:contract",
|
"test:contract": "npm --workspace @logicsrc/commandboard-api run test:contract && npm --workspace @logicsrc/web run test:contract",
|
||||||
"test:e2e": "npm --workspace @logicsrc/commandboard-web run test:e2e"
|
"test:e2e": "npm --workspace @logicsrc/commandboard-web run test:e2e && npm --workspace @logicsrc/web run test:e2e"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^24.10.1",
|
"@types/node": "^24.10.1",
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue