diff --git a/README.md b/README.md index 24a582d..9975810 100644 --- a/README.md +++ b/README.md @@ -78,7 +78,7 @@ The following services are exposed with this addon: | Service | URL | Notes | |-------------------------|-----------------------------------|--------------------------------------------------------------------------------------------| -| KasmVNC | https://\.ddev.site:8444 | Username is your local username. Password is `secret`. | +| KasmVNC | https://\.ddev.site:8444 | No login required. | | Playwright Test Reports | https://\.ddev.site:9324 | This port is changed from the default to not conflict with running Playwright on the host. | ## Viewing test reports diff --git a/config.playwright.yml b/config.playwright.yml index 39ea2bd..40dddd4 100644 --- a/config.playwright.yml +++ b/config.playwright.yml @@ -85,7 +85,8 @@ web_environment: # We add the sleep so this doesn't error out when not using playwright. web_extra_daemons: - name: "kasmvnc" - command: "kasmvncserver -fg || sleep infinity" + # Basic Auth is disabled for both HTTP and the desktop WebSocket. + command: "kasmvncserver -fg -disableBasicAuth || sleep infinity" directory: /var/www/html web_extra_exposed_ports: - name: playwright diff --git a/tests/test.bats b/tests/test.bats index c7315a7..3dd5f7e 100644 --- a/tests/test.bats +++ b/tests/test.bats @@ -121,6 +121,7 @@ verify_run_playwright() { mkdir -p "${playwright_dir}/tests" cp "$DIR"/tests/testdata/phpinfo.spec.ts "${playwright_dir}/tests/phpinfo.spec.ts" + cp "$DIR"/tests/testdata/kasmvnc.spec.ts "${playwright_dir}/tests/kasmvnc.spec.ts" health_checks # Both paths expose the same tmpfs: the namespaced path is preferred, while @@ -129,12 +130,11 @@ verify_run_playwright() { assert_success ddev exec -- rm -f /tmp/ddev-playwright/compatibility-check - # Verify kasmvnc is listening. - curl -s https://"${PROJNAME}".ddev.site:8444/ - curl -s --user "$USER":secret https://"${PROJNAME}.ddev.site:8444/" - ddev logs - echo "#" curl -s --user "$USER":secret https://"${PROJNAME}.ddev.site:8444/" >&3 - curl -s --user "$USER":secret https://"${PROJNAME}.ddev.site:8444/" | grep -q KasmVNC + # The HTTP client must load without credentials or an authentication challenge. + run curl --fail --silent --show-error --dump-header - "https://${PROJNAME}.ddev.site:8444/" + assert_success + assert_output --partial "KasmVNC" + refute_output --partial "WWW-Authenticate:" # Verify that browsers have been downloaded. # @@ -160,10 +160,16 @@ verify_run_playwright() { assert_invariant_layers_precede_browser_install - # Verify we can run an example test. The reporters come from - # playwright.config.ts (line + html); the html one leaves behind a report for - # verify_show_report to serve. - ddev playwright test + # All KasmVNC clients control the same X display. Concurrent clients would + # interleave keyboard input in the focused window, so serialize only this + # desktop-control check. The add-on does not restrict downstream workers. + ddev playwright test kasmvnc.spec.ts --workers=1 --reporter=line + + # Application tests have separate browser contexts and can run concurrently. + # Explicitly use three workers so CI exercises parallel application testing + # across Chromium, Firefox and WebKit, even when the fixture's CI default is + # one worker. Keep its line + html reporters for verify_show_report below. + ddev playwright test phpinfo.spec.ts --workers=3 verify_show_report } diff --git a/tests/testdata/kasmvnc.spec.ts b/tests/testdata/kasmvnc.spec.ts new file mode 100644 index 0000000..25591ac --- /dev/null +++ b/tests/testdata/kasmvnc.spec.ts @@ -0,0 +1,50 @@ +import { test, expect, chromium } from '@playwright/test'; +import { randomUUID } from 'node:crypto'; + +// Runs in Chromium, Firefox and WebKit, with no httpCredentials configured. +// Loading the HTML alone misses failures in WebSocket auth or RFB negotiation. +// KasmVNC is already running (test.bats checks HTTP readiness), so connection +// setup uses the fixture's normal test and assertion timeouts. +test('KasmVNC connects to the desktop without credentials', async ({ page }) => { + // This headed browser receives input on KasmVNC's X display. Its DOM gives + // us a direct assertion of remote input without desktop screenshot timing. + const desktopBrowser = await chromium.launch({ headless: false, args: ['--kiosk'] }); + try { + const desktopPage = await desktopBrowser.newPage(); + await desktopPage.setContent(` + + + `); + await desktopPage.bringToFront(); + const input = desktopPage.getByRole('textbox'); + await input.focus(); + + const url = new URL(process.env.DDEV_PRIMARY_URL!); + url.port = '8444'; + url.searchParams.set('autoconnect', '1'); + + const response = await page.goto(url.toString()); + expect(response?.status()).toBe(200); + expect(response?.headers()['www-authenticate']).toBeUndefined(); + await expect(page.locator('html')).toHaveClass(/noVNC_connected/); + + // A completed RFB connection must have received the desktop dimensions. + const canvas = page.locator('#noVNC_container canvas').first(); + await expect(canvas).toBeVisible(); + await expect.poll(() => canvas.evaluate((element: HTMLCanvasElement) => + element.width > 0 && element.height > 0 + )).toBe(true); + + // The kiosk window fills the desktop with the textarea. A remote click + // focuses it, then KasmVNC transports the keystrokes to that window. + await canvas.click(); + const text = `kasmvnc-${randomUUID()}`; + await page.keyboard.type(text, { delay: 20 }); + await expect(input).toHaveValue(text); + } finally { + await desktopBrowser.close(); + } +}); diff --git a/web-build/disabled.Dockerfile.playwright b/web-build/disabled.Dockerfile.playwright index 8aa2781..6022c7e 100644 --- a/web-build/disabled.Dockerfile.playwright +++ b/web-build/disabled.Dockerfile.playwright @@ -85,7 +85,9 @@ COPY kasmvnc.yaml xstartup /home/$username/.vnc RUN chown $username:$username /home/$username/.vnc/* RUN sudo -u $username touch /home/$username/.vnc/.de-was-selected -RUN sudo -u $username /bin/bash -c 'echo -e "secret\nsecret\n" | kasmvncpasswd -wo -u $USER' +# KasmVNC 1.4.0's launcher requires an account even with -disableBasicAuth. +# This random credential only satisfies that startup check; clients do not use it. +RUN sudo -u $username /bin/bash -c 'password=$(openssl rand -hex 32); printf "%s\n%s\n" "$password" "$password" | kasmvncpasswd -wo -u "$USER"' # --------------------------------------------------------------------------- # End of VNC / desktop stack.