frontend-harness 0.6.3 → 0.6.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/dist/cli/index.js +2 -2
- package/dist/cli/index.js.map +1 -1
- package/dist/runtime/knowledge.js +18 -7
- package/dist/runtime/knowledge.js.map +1 -1
- package/dist/runtime/policy-provenance.js +13 -1
- package/dist/runtime/policy-provenance.js.map +1 -1
- package/dist/runtime/scaffold/template-renderer.d.ts +26 -0
- package/dist/runtime/scaffold/template-renderer.js +28 -0
- package/dist/runtime/scaffold/template-renderer.js.map +1 -0
- package/dist/runtime/scaffold/vue-template.d.ts +1 -5
- package/dist/runtime/scaffold/vue-template.js +43 -95
- package/dist/runtime/scaffold/vue-template.js.map +1 -1
- package/dist/runtime/scaffold.d.ts +1 -0
- package/dist/runtime/scaffold.js +13 -3
- package/dist/runtime/scaffold.js.map +1 -1
- package/package.json +2 -1
- package/templates/scaffold/vue/.husky/pre-commit +1 -0
- package/templates/scaffold/vue/README.md.tpl +50 -0
- package/templates/scaffold/vue/eslint.config.mjs +49 -0
- package/templates/scaffold/vue/gitignore.tpl +23 -0
- package/templates/scaffold/vue/index.html.tpl +12 -0
- package/templates/scaffold/vue/playwright.config.mjs +29 -0
- package/templates/scaffold/vue/postcss.config.mjs +5 -0
- package/templates/scaffold/vue/src/App.vue +7 -0
- package/templates/scaffold/vue/src/env.d.ts +7 -0
- package/templates/scaffold/vue/src/features/home/HomeView.vue +86 -0
- package/templates/scaffold/vue/src/main.ts +5 -0
- package/templates/scaffold/vue/src/service/http.ts +18 -0
- package/templates/scaffold/vue/src/service/index.ts +1 -0
- package/templates/scaffold/vue/src/storage/index.ts +31 -0
- package/templates/scaffold/vue/src/styles/main.scss +21 -0
- package/templates/scaffold/vue/src/styles/tokens.scss +20 -0
- package/templates/scaffold/vue/src/utils/index.ts +11 -0
- package/templates/scaffold/vue/tests/e2e/app-desktop.spec.ts +10 -0
- package/templates/scaffold/vue/tests/e2e/app-mobile.spec.ts +9 -0
- package/templates/scaffold/vue/tests/integration/app.test.ts +15 -0
- package/templates/scaffold/vue/tests/render.ts +6 -0
- package/templates/scaffold/vue/tests/setup.ts +7 -0
- package/templates/scaffold/vue/vite.config.mjs +33 -0
- package/templates/scaffold/vue/vitest.config.mjs +34 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
type StorageKind = "local" | "session";
|
|
2
|
+
|
|
3
|
+
function resolveStorage(kind: StorageKind): Storage {
|
|
4
|
+
return kind === "session" ? sessionStorage : localStorage;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function createStorage(kind: StorageKind = "local") {
|
|
8
|
+
const storage = resolveStorage(kind);
|
|
9
|
+
|
|
10
|
+
return {
|
|
11
|
+
get<T>(key: string): T | null {
|
|
12
|
+
const raw = storage.getItem(key);
|
|
13
|
+
if (raw === null) return null;
|
|
14
|
+
try {
|
|
15
|
+
return JSON.parse(raw) as T;
|
|
16
|
+
} catch (error) {
|
|
17
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
18
|
+
throw new Error(`Storage value for ${key} is not valid JSON: ${message}`);
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
set<T>(key: string, value: T): void {
|
|
22
|
+
storage.setItem(key, JSON.stringify(value));
|
|
23
|
+
},
|
|
24
|
+
remove(key: string): void {
|
|
25
|
+
storage.removeItem(key);
|
|
26
|
+
},
|
|
27
|
+
clear(): void {
|
|
28
|
+
storage.clear();
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
@use "./tokens" as *;
|
|
2
|
+
|
|
3
|
+
:root {
|
|
4
|
+
color-scheme: light;
|
|
5
|
+
font-family: Inter, "PingFang SC", "Microsoft YaHei", sans-serif;
|
|
6
|
+
line-height: 1.5;
|
|
7
|
+
font-weight: 400;
|
|
8
|
+
font-synthesis: none;
|
|
9
|
+
text-rendering: optimizeLegibility;
|
|
10
|
+
-webkit-font-smoothing: antialiased;
|
|
11
|
+
-moz-osx-font-smoothing: grayscale;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
* { box-sizing: border-box; }
|
|
15
|
+
|
|
16
|
+
html, body, #app {
|
|
17
|
+
min-height: 100%;
|
|
18
|
+
margin: 0;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
body { background: $color-surface-muted; }
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
$color-surface: #ffffff;
|
|
2
|
+
$color-surface-muted: #f6f8fb;
|
|
3
|
+
$color-border: #d7deea;
|
|
4
|
+
$color-text: #102030;
|
|
5
|
+
$color-text-muted: #5f6b7a;
|
|
6
|
+
$color-primary: #2563eb;
|
|
7
|
+
$color-success: #16a34a;
|
|
8
|
+
$color-warning: #d97706;
|
|
9
|
+
$color-danger: #dc2626;
|
|
10
|
+
|
|
11
|
+
$space-1: 4px;
|
|
12
|
+
$space-2: 8px;
|
|
13
|
+
$space-3: 12px;
|
|
14
|
+
$space-4: 16px;
|
|
15
|
+
$space-5: 20px;
|
|
16
|
+
$space-6: 24px;
|
|
17
|
+
|
|
18
|
+
$radius-control: 6px;
|
|
19
|
+
$radius-card: 8px;
|
|
20
|
+
$table-min-width: 960px;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export function isNil(value: unknown): value is null | undefined {
|
|
2
|
+
return value === null || value === undefined;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export function isBlank(value: unknown): value is string {
|
|
6
|
+
return typeof value === "string" && value.trim().length === 0;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function pickDefined<T extends Record<string, unknown>>(value: T): Partial<T> {
|
|
10
|
+
return Object.fromEntries(Object.entries(value).filter(([, item]) => item !== undefined)) as Partial<T>;
|
|
11
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { expect, test } from "@playwright/test";
|
|
2
|
+
|
|
3
|
+
test("desktop smoke renders the scaffold shell", async ({ page }) => {
|
|
4
|
+
await page.goto("/");
|
|
5
|
+
|
|
6
|
+
await expect(page.getByRole("heading", { level: 1, name: "Engineering base is ready" })).toBeVisible();
|
|
7
|
+
await expect(page.getByRole("heading", { level: 2, name: "utils" })).toBeVisible();
|
|
8
|
+
await expect(page.getByRole("heading", { level: 2, name: "storage" })).toBeVisible();
|
|
9
|
+
await expect(page.getByRole("heading", { level: 2, name: "service" })).toBeVisible();
|
|
10
|
+
});
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { expect, test } from "@playwright/test";
|
|
2
|
+
|
|
3
|
+
test("mobile smoke keeps scaffold content in the viewport", async ({ page }) => {
|
|
4
|
+
await page.goto("/");
|
|
5
|
+
|
|
6
|
+
await expect(page.getByRole("heading", { level: 1, name: "Engineering base is ready" })).toBeVisible();
|
|
7
|
+
const documentOverflow = await page.evaluate(() => document.documentElement.scrollWidth > document.documentElement.clientWidth);
|
|
8
|
+
expect(documentOverflow).toBe(false);
|
|
9
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { screen } from "@testing-library/vue";
|
|
2
|
+
import { describe, expect, it } from "vitest";
|
|
3
|
+
import App from "../../src/App.vue";
|
|
4
|
+
import { renderPage } from "../render";
|
|
5
|
+
|
|
6
|
+
describe("scaffold app", () => {
|
|
7
|
+
it("renders the engineering baseline sections", () => {
|
|
8
|
+
renderPage(App);
|
|
9
|
+
|
|
10
|
+
expect(screen.getByRole("heading", { level: 1, name: "Engineering base is ready" })).toBeInTheDocument();
|
|
11
|
+
expect(screen.getByRole("heading", { level: 2, name: "utils" })).toBeInTheDocument();
|
|
12
|
+
expect(screen.getByRole("heading", { level: 2, name: "storage" })).toBeInTheDocument();
|
|
13
|
+
expect(screen.getByRole("heading", { level: 2, name: "service" })).toBeInTheDocument();
|
|
14
|
+
});
|
|
15
|
+
});
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { defineConfig } from "vite";
|
|
2
|
+
import vue from "@vitejs/plugin-vue";
|
|
3
|
+
import AutoImport from "unplugin-auto-import/vite";
|
|
4
|
+
import Components from "unplugin-vue-components/vite";
|
|
5
|
+
import { ElementPlusResolver } from "unplugin-vue-components/resolvers";
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
import { fileURLToPath } from "node:url";
|
|
8
|
+
|
|
9
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
10
|
+
|
|
11
|
+
export default defineConfig({
|
|
12
|
+
plugins: [
|
|
13
|
+
vue(),
|
|
14
|
+
AutoImport({
|
|
15
|
+
imports: ["vue"],
|
|
16
|
+
dts: "src/types/auto-imports.d.ts",
|
|
17
|
+
resolvers: [ElementPlusResolver()]
|
|
18
|
+
}),
|
|
19
|
+
Components({
|
|
20
|
+
dts: "src/types/components.d.ts",
|
|
21
|
+
resolvers: [ElementPlusResolver()]
|
|
22
|
+
})
|
|
23
|
+
],
|
|
24
|
+
resolve: {
|
|
25
|
+
alias: {
|
|
26
|
+
"@": path.resolve(__dirname, "src")
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
server: {
|
|
30
|
+
host: true,
|
|
31
|
+
hmr: { overlay: false }
|
|
32
|
+
}
|
|
33
|
+
});
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { defineConfig } from "vitest/config";
|
|
2
|
+
import vue from "@vitejs/plugin-vue";
|
|
3
|
+
import AutoImport from "unplugin-auto-import/vite";
|
|
4
|
+
import Components from "unplugin-vue-components/vite";
|
|
5
|
+
import { ElementPlusResolver } from "unplugin-vue-components/resolvers";
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
import { fileURLToPath } from "node:url";
|
|
8
|
+
|
|
9
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
10
|
+
|
|
11
|
+
export default defineConfig({
|
|
12
|
+
plugins: [
|
|
13
|
+
vue(),
|
|
14
|
+
AutoImport({
|
|
15
|
+
imports: ["vue"],
|
|
16
|
+
dts: false,
|
|
17
|
+
resolvers: [ElementPlusResolver()]
|
|
18
|
+
}),
|
|
19
|
+
Components({
|
|
20
|
+
dts: false,
|
|
21
|
+
resolvers: [ElementPlusResolver()]
|
|
22
|
+
})
|
|
23
|
+
],
|
|
24
|
+
resolve: {
|
|
25
|
+
alias: {
|
|
26
|
+
"@": path.resolve(__dirname, "src")
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
test: {
|
|
30
|
+
environment: "jsdom",
|
|
31
|
+
include: ["tests/**/*.test.ts"],
|
|
32
|
+
setupFiles: ["tests/setup.ts"]
|
|
33
|
+
}
|
|
34
|
+
});
|