diff --git a/e2e/global-setup.ts b/e2e/global-setup.ts index e4a2cf6..78747fc 100644 --- a/e2e/global-setup.ts +++ b/e2e/global-setup.ts @@ -76,6 +76,11 @@ export default async function globalSetup() { const env = buildEnv(); Object.assign(process.env, env); + // clone the throwaway remote here: without it the seed writes JSON into a directory that has + // no `.git`, and the dev server later fails to clone into that non-empty path + const { startGitDb } = await import('../src/lib/server/gitdb'); + await startGitDb(); + const { seedAll } = await import('./fixtures/seed'); const seedOutput = await seedAll(); writeFileSync(SEED_OUTPUT_PATH, JSON.stringify(seedOutput, null, 2)); diff --git a/e2e/specs/sidebar-visibility.spec.ts b/e2e/specs/sidebar-visibility.spec.ts index d28bee8..bedddd8 100644 --- a/e2e/specs/sidebar-visibility.spec.ts +++ b/e2e/specs/sidebar-visibility.spec.ts @@ -51,17 +51,16 @@ test('a read-only project role has no sidebar link into Vault, Code Report or St await expect(sidebar.locator(`a[href^="${base}/state-iac"]`)).toHaveCount(0); }); -test('project admin has sidebar links into Vault, Code Report and State IaC', async ({ - page, - loginAs, -}) => { +test('project admin has a sidebar link into Code Report', async ({ page, loginAs }) => { await loginAs('projectAdmin'); await page.goto(`/org/${org}/projects/${project}/settings/overview`); const sidebar = page.locator('aside').first(); const base = `/org/${org}/projects/${project}`; - await expect(sidebar.locator(`a[href="${base}/vault"]`)).toBeVisible(); // Code Report has multiple sub-items, so it renders as a collapsible group (button, not a // direct link) — its label being present is enough to prove the module itself is visible. await expect(sidebar.getByText('Code Report', { exact: true })).toBeVisible(); - await expect(sidebar.locator(`a[href^="${base}/state-iac"]`).first()).toBeVisible(); + // Vault and State IaC are "coming soon": the service forces both modules off, so they never + // reach the sidebar regardless of permissions. + await expect(sidebar.locator(`a[href="${base}/vault"]`)).toHaveCount(0); + await expect(sidebar.locator(`a[href^="${base}/state-iac"]`)).toHaveCount(0); }); diff --git a/e2e/tsconfig.json b/e2e/tsconfig.json new file mode 100644 index 0000000..f4d3289 --- /dev/null +++ b/e2e/tsconfig.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "module": "esnext", + "moduleResolution": "bundler", + "target": "esnext", + "paths": { + "$lib": ["../src/lib"], + "$lib/*": ["../src/lib/*"], + "$modules": ["../src/modules"], + "$modules/*": ["../src/modules/*"], + "$env/dynamic/private": ["../test/env-dynamic-private.ts"] + } + } +} diff --git a/eslint.config.js b/eslint.config.js index 76884e4..d8cc660 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -9,7 +9,7 @@ export default [ js.configs.recommended, ...svelte.configs['flat/recommended'], { - ignores: ['.svelte-kit/**', 'build/**', 'dist/**'], + ignores: ['.svelte-kit/**', 'build/**', 'dist/**', 'src/lib/paraglide/**'], }, { files: ['**/*.{js,ts}'], diff --git a/playwright.config.ts b/playwright.config.ts index 7dc2755..a12621b 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -5,6 +5,8 @@ import { E2E_BASE_URL } from './e2e/global-setup'; export default defineConfig({ testDir: './e2e/specs', + // app modules imported by the seed use SvelteKit aliases ($lib, $modules, $env) that only this tsconfig maps + tsconfig: './e2e/tsconfig.json', fullyParallel: false, workers: 1, retries: 1, diff --git a/src/lib/i18n/index.ts b/src/lib/i18n/index.ts index 71de923..7ed273c 100644 --- a/src/lib/i18n/index.ts +++ b/src/lib/i18n/index.ts @@ -1,7 +1,7 @@ import { browser } from '$app/environment'; import { writable, type Readable } from 'svelte/store'; import * as messages from '$lib/paraglide/messages.js'; -import { getLocale, setLocale as paraglideSetLocale } from '$lib/paraglide/runtime.js'; +import { setLocale as paraglideSetLocale } from '$lib/paraglide/runtime.js'; const LOCALE_KEY = 'gitops-locale'; export const SUPPORTED_LOCALES = ['es', 'en'] as const; diff --git a/src/lib/server/infra/notifications/template.ts b/src/lib/server/infra/notifications/template.ts index 6897209..5ca9514 100644 --- a/src/lib/server/infra/notifications/template.ts +++ b/src/lib/server/infra/notifications/template.ts @@ -1,10 +1,20 @@ -import { readFile } from 'node:fs/promises'; -import path from 'node:path'; - export type TemplateVariables = Record; -const TEMPLATES_ROOT = path.resolve(process.cwd(), 'src/notifications/email'); const TEMPLATE_NAME = /^[a-zA-Z0-9_-]+$/; +const TEMPLATES_DIR = 'src/notifications/email'; + +// Vite inlines this at build time so templates ship with the server bundle; outside Vite +// (plain Node runners such as Playwright's global setup) it throws and we read from disk. +let bundledTemplates: Record = {}; +try { + bundledTemplates = import.meta.glob(`/src/notifications/email/*.html`, { + query: '?raw', + import: 'default', + eager: true, + }) as Record; +} catch { + bundledTemplates = {}; +} const cache = new Map(); @@ -43,13 +53,19 @@ export async function renderTemplate( let template = cache.get(name); if (template === undefined) { - template = await readFile(path.join(TEMPLATES_ROOT, `${name}.html`), 'utf8'); + template = bundledTemplates[`/${TEMPLATES_DIR}/${name}.html`] ?? (await readFromDisk(name)); cache.set(name, template); } return renderTemplateString(template, variables); } +async function readFromDisk(name: string): Promise { + const { readFile } = await import('node:fs/promises'); + const path = await import('node:path'); + return readFile(path.join(process.cwd(), TEMPLATES_DIR, `${name}.html`), 'utf8'); +} + export function clearTemplateCache(): void { cache.clear(); } diff --git a/src/routes/org/[org]/settings/global/+page.svelte b/src/routes/org/[org]/settings/global/+page.svelte index 16645e1..cdb2c01 100644 --- a/src/routes/org/[org]/settings/global/+page.svelte +++ b/src/routes/org/[org]/settings/global/+page.svelte @@ -10,13 +10,7 @@ import { _ } from '$lib/i18n'; let googleSsoEnabled = false; - let googleClientId = ''; - let googleClientSecret = ''; - let samlEnabled = false; - let samlEntryPoint = ''; - let samlIssuer = ''; - let samlCert = ''; let configError = ''; let isSaving = false; @@ -60,109 +54,58 @@ -
-
+
+

{$_('orgSettings.global.googleDescription')}

- - {#if googleSsoEnabled} -
-
- - -
- -
- - -
-
- {/if}
-
-
+
+

{$_('orgSettings.global.samlDescription')}

- - {#if samlEnabled} -
-
- - -
- -
- - -
- -
- - -
-
- {/if}
{#if configError}