From 34995ce399b35bbd7a4a6c5a6de155f5e41d922b Mon Sep 17 00:00:00 2001 From: Michal Klos Date: Mon, 29 Sep 2025 11:20:22 +0200 Subject: [PATCH 1/2] test: kw-bridge test --- .gitignore | 1 + packages/web-container/config.kw-bridge.json | 25 ++++++ tests/e2e-playwright/specs/kw-bridge.spec.ts | 83 ++++++++++++++++++++ 3 files changed, 109 insertions(+) create mode 100644 packages/web-container/config.kw-bridge.json create mode 100644 tests/e2e-playwright/specs/kw-bridge.spec.ts diff --git a/.gitignore b/.gitignore index 3140faaa60c..42224cabfa4 100644 --- a/.gitignore +++ b/.gitignore @@ -55,6 +55,7 @@ playwright-report tests/e2e/cucumber/report/* !tests/e2e/cucumber/report/index.mjs .vscode/settings.json +test-results # third party licenses /third-party-licenses diff --git a/packages/web-container/config.kw-bridge.json b/packages/web-container/config.kw-bridge.json new file mode 100644 index 00000000000..7fe8609339c --- /dev/null +++ b/packages/web-container/config.kw-bridge.json @@ -0,0 +1,25 @@ +{ + "server": "http://localhost:8888", + "theme": "http://localhost:8888/themes/owncloud/theme.json", + "openIdConnect": { + "metadata_url": "http://localhost:8888/.well-known/openid-configuration", + "authority": "http://localhost:8888/", + "client_id": "AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEE", + "response_type": "code", + "client_secret": "FFFFFFFFFFFF", + "fetchRequestCredentials": "omit" + }, + "apps": [ + "files", + "preview", + "pdf-viewer", + "search", + "text-editor", + "external", + "admin-settings", + "epub-reader" + ], + "options": { + "contextHelpersReadMore": true + } +} diff --git a/tests/e2e-playwright/specs/kw-bridge.spec.ts b/tests/e2e-playwright/specs/kw-bridge.spec.ts new file mode 100644 index 00000000000..3a948e25a41 --- /dev/null +++ b/tests/e2e-playwright/specs/kw-bridge.spec.ts @@ -0,0 +1,83 @@ +import { expect, test } from '@playwright/test' +import { config as e2eConfig } from '../../e2e/config.js' +import { promises as fs } from 'fs' +import path from 'path' + +// This test overrides the runtime config by intercepting /config.json and fulfilling it +// with the contents of the repo's web container config. It then opens the oCIS home +// page and takes a screenshot once the page has finished loading. +test('kw-bridge with config override.json', async ({ page }) => { + const overrideConfigPath = path.resolve(process.cwd(), 'packages/web-container/config.kw-bridge.json') + try { + await fs.access(overrideConfigPath) + } catch { + throw new Error('Missing packages/web-container/config.kw-bridge.json') + } + + await page.route('**/config.json', async (route) => { + const raw = await fs.readFile(overrideConfigPath, 'utf-8') + const payload = JSON.parse(raw) + await route.fulfill({ + contentType: 'application/json', + body: JSON.stringify(payload) + }) + }) + + const response = await page.goto(e2eConfig.baseUrl, { waitUntil: 'load' }) + + // Assert main navigation succeeded (no 404/5xx) + expect(response, 'navigation response should be defined').toBeTruthy() + expect(response!.ok(), `Expected navigation 2xx/3xx but got ${response?.status()} ${response?.statusText()}`).toBeTruthy() + + await page.waitForLoadState('networkidle') + + // Sanity check content does not include common 404 markers + const html = await page.content() + expect(html).not.toMatch(/\b(404|not found)\b/i) + + // ------------------------------------------------------------ + // Login view + // Email input + const login = process.env.KW_LOGIN + const password = process.env.KW_PASSWORD + expect(login, 'KW_LOGIN env var is required').toBeTruthy() + expect(password, 'KW_PASSWORD env var is required').toBeTruthy() + + const emailInput = page.locator('#email') + await expect(emailInput).toBeVisible() // assert that Kiteworks login view loaded + await emailInput.waitFor({ state: 'visible' }) + await emailInput.fill(login!) + await expect(emailInput).toHaveValue(login!) + + // Press "Next" + await page.click('button:has-text("Next")') + + // Fill password + await page.fill('#password', password!) + + // Press "Sign in" + await page.waitForSelector('button:has-text("Sign in")', { state: 'visible' }) + await page.click('button:has-text("Sign in")') + + // ------------------------------------------------------------ + // Grant view + await page.locator('#btnAccept').waitFor({ state: 'visible' }) + await page.click('#btnAccept') + + // ------------------------------------------------------------ + // Home space view + // Wait for the space files table to load + const spaceFilesTable = page.locator('tbody.has-item-context-menu'); + await spaceFilesTable.waitFor({ state: 'visible' }); + + // Verify the presence of a specific file in the table + const fileName = 'upload-PUT.txt'; + await expect(page.locator('main#files')).toContainText(fileName, { timeout: 30000 }); + + + // ------------------------------------------------------------ + // Attach screenshot to the test report to confirm the rendered result + const screenshot = await page.screenshot({ fullPage: true }) + await test.info().attach('home-screenshot', { body: screenshot, contentType: 'image/png' }) +}) + From 18ce4d3c073a3a1ec39d939e4c02b28c44e5712a Mon Sep 17 00:00:00 2001 From: Michal Klos Date: Tue, 30 Sep 2025 16:54:35 +0200 Subject: [PATCH 2/2] fix: http 502, serve config.json from file --- vite.config.ts | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/vite.config.ts b/vite.config.ts index 4c2fa675b9d..7e4fd0ac659 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -85,6 +85,19 @@ const getConfigJson = async (url: string) => { return (await getJson(url)) as ConfigJsonResponseBody } +const isHttpUrl = (value: string) => /^https?:\/\//i.test(value) + +const getConfigFromUrlOrFile = async (value: string): Promise => { + if (isHttpUrl(value)) { + return await getConfigJson(value) + } + + // Treat non-http(s) values as filesystem paths relative to the dev server cwd + const filePath = value.startsWith('/') ? value : join(process.cwd(), value) + const raw = readFileSync(filePath, 'utf8') + return JSON.parse(raw) as ConfigJsonResponseBody +} + export const historyModePlugins = () => [ { @@ -258,7 +271,7 @@ export default defineConfig(({ mode, command }) => { server.middlewares.use(async (request, response, next) => { if (request.url === '/config.json') { try { - const configJson = await getConfigJson(configUrl) + const configJson = await getConfigFromUrlOrFile(configUrl) response.statusCode = 200 response.setHeader('Content-Type', 'application/json') response.end(JSON.stringify(configJson))