| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240 |
- /** Assembled keyless snapshot for the default `dsh web` browser handoff. */
- import { existsSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs'
- import { tmpdir } from 'node:os'
- import { join } from 'node:path'
- import { fileURLToPath } from 'node:url'
- import { execa } from 'execa'
- import { afterEach, describe, expect, it } from 'vitest'
- const repoRoot = fileURLToPath(new URL('../../../', import.meta.url))
- const builtBin = join(repoRoot, 'apps/cli/lib/bin.js')
- const frontendIndex = join(repoRoot, 'apps/web/dist/index.html')
- const openerHook = new URL('./fixtures/web-browser-open/register.mjs', import.meta.url).href
- const openingMessage = 'dsh web: opening the default browser; pass --no-open to disable'
- const tempRoots: string[] = []
- const builtArtifactsExist = existsSync(builtBin) && existsSync(frontendIndex)
- if (process.env.DSH_EXAMPLE_MODE === 'lib' && !builtArtifactsExist) {
- throw new Error('dsh web browser-open snapshot requires built CLI and Web artifacts in lib mode')
- }
- afterEach(() => {
- for (const root of tempRoots.splice(0)) rmSync(root, { recursive: true, force: true })
- })
- interface BrowserOpenRecord {
- url: string
- status: number
- bootManifest: boolean
- apiKeyPresent: boolean
- dshHomePresent: boolean
- }
- function normalizeLocalUrl(url: string): string {
- return url
- .replace(/:\d+/u, ':{{port}}')
- .replace(/token=[^&]+/u, 'token={{token}}')
- }
- describe.skipIf(!builtArtifactsExist)('dsh web browser-open assembled snapshot', () => {
- it('hands the reachable page to the default browser after the shipped tree settles', async () => {
- const root = mkdtempSync(join(tmpdir(), 'dsh-web-browser-open-snapshot-'))
- tempRoots.push(root)
- const result = await execa(process.execPath, [
- '--import', openerHook,
- builtBin,
- 'web',
- '--port', '0',
- ], {
- cwd: root,
- env: {
- ...process.env,
- DEEPSEEK_API_KEY: 'keyless-browser-open-no-call',
- DSH_AGENTS_HOME: join(root, '.agents'),
- DSH_HOME: join(root, '.dsh'),
- DSH_TELEMETRY_DISABLED: '1',
- NODE_NO_WARNINGS: '1',
- SSH_CONNECTION: '',
- SSH_TTY: '',
- },
- input: '',
- timeout: 30_000,
- killSignal: 'SIGKILL',
- reject: false,
- })
- const readyUrl = /dsh web: (http:\/\/[^\s]+)/u.exec(result.stdout)?.[1]
- const openLine = result.stdout.split('\n').find(line => line.startsWith('dsh browser-open: '))
- const opening = result.stdout.includes(openingMessage)
- if (readyUrl === undefined || openLine === undefined || !opening) {
- throw new Error(`dsh web browser-open evidence missing\nstdout:\n${result.stdout}\nstderr:\n${result.stderr}`)
- }
- const opened = JSON.parse(openLine.slice('dsh browser-open: '.length)) as BrowserOpenRecord
- expect({
- exitCode: result.exitCode,
- opening,
- readyUrl: normalizeLocalUrl(readyUrl),
- openedUrl: normalizeLocalUrl(opened.url),
- status: opened.status,
- bootManifest: opened.bootManifest,
- apiKeyPresent: opened.apiKeyPresent,
- dshHomePresent: opened.dshHomePresent,
- stderr: result.stderr,
- }).toMatchInlineSnapshot(`
- {
- "apiKeyPresent": false,
- "bootManifest": true,
- "dshHomePresent": false,
- "exitCode": 0,
- "openedUrl": "http://127.0.0.1:{{port}}/?token={{token}}",
- "opening": true,
- "readyUrl": "http://127.0.0.1:{{port}}/?token={{token}}",
- "status": 200,
- "stderr": "",
- }
- `)
- })
- it('prints the launcher reason and manual URL after the Web app is ready', async () => {
- const root = mkdtempSync(join(tmpdir(), 'dsh-web-browser-open-failure-snapshot-'))
- tempRoots.push(root)
- const result = await execa(process.execPath, [
- '--import', openerHook,
- builtBin,
- 'web',
- '--port', '0',
- ], {
- cwd: root,
- env: {
- ...process.env,
- BROWSER_OPEN_TEST_FAILURE: 'fixture desktop unavailable',
- DEEPSEEK_API_KEY: 'keyless-browser-open-no-call',
- DSH_AGENTS_HOME: join(root, '.agents'),
- DSH_BROWSER_OPEN_TEST_EXIT_ON_FAILURE: '1',
- DSH_HOME: join(root, '.dsh'),
- DSH_TELEMETRY_DISABLED: '1',
- NODE_NO_WARNINGS: '1',
- SSH_CONNECTION: '',
- SSH_TTY: '',
- },
- input: '',
- timeout: 30_000,
- killSignal: 'SIGKILL',
- reject: false,
- })
- const readyUrl = /dsh web: (http:\/\/[^\s]+)/u.exec(result.stdout)?.[1]
- const diagnostic = result.stderr.split(/\r?\n/u)
- .find(line => line.startsWith('web-app: could not open the default browser because '))
- expect({
- diagnostic,
- exitCode: result.exitCode,
- opened: result.stdout.includes('dsh browser-open: '),
- opening: result.stdout.includes(openingMessage),
- readyUrl: readyUrl === undefined ? undefined : normalizeLocalUrl(readyUrl),
- }).toMatchInlineSnapshot(`
- {
- "diagnostic": "web-app: could not open the default browser because fixture desktop unavailable; use the dsh web URL printed at startup",
- "exitCode": 0,
- "opened": false,
- "opening": true,
- "readyUrl": "http://127.0.0.1:{{port}}/?token={{token}}",
- }
- `)
- })
- it('prints the host URL without launching a browser in a VS Code Remote SSH session', async () => {
- const root = mkdtempSync(join(tmpdir(), 'dsh-web-browser-open-ssh-snapshot-'))
- tempRoots.push(root)
- const result = await execa(process.execPath, [
- '--import', openerHook,
- builtBin,
- 'web',
- '--port', '0',
- ], {
- cwd: root,
- env: {
- ...process.env,
- DEEPSEEK_API_KEY: 'keyless-browser-open-no-call',
- DSH_AGENTS_HOME: join(root, '.agents'),
- DSH_BROWSER_OPEN_TEST_EXIT_ON_READY: '1',
- DSH_HOME: join(root, '.dsh'),
- DSH_TELEMETRY_DISABLED: '1',
- NODE_NO_WARNINGS: '1',
- SSH_CONNECTION: '10.0.0.2 55000 10.0.0.9 22',
- SSH_TTY: '',
- VSCODE_IPC_HOOK_CLI: '/tmp/vscode-ipc',
- },
- input: '',
- timeout: 30_000,
- killSignal: 'SIGKILL',
- reject: false,
- })
- const readyUrl = /dsh web: (http:\/\/[^\s]+)/u.exec(result.stdout)?.[1]
- expect({
- exitCode: result.exitCode,
- opening: result.stdout.includes(openingMessage),
- readyUrl: readyUrl === undefined ? undefined : normalizeLocalUrl(readyUrl),
- opened: result.stdout.includes('dsh browser-open: '),
- stderr: result.stderr,
- }).toMatchInlineSnapshot(`
- {
- "exitCode": 0,
- "opened": false,
- "opening": false,
- "readyUrl": "http://127.0.0.1:{{port}}/?token={{token}}",
- "stderr": "",
- }
- `)
- })
- it('rejects a project browser command before starting the Web app', async () => {
- const root = mkdtempSync(join(tmpdir(), 'dsh-web-browser-open-env-snapshot-'))
- tempRoots.push(root)
- writeFileSync(join(root, '.env'), 'BROWSER=./project-browser\n')
- const result = await execa(process.execPath, [
- '--import', openerHook,
- builtBin,
- 'web',
- '--port', '0',
- ], {
- cwd: root,
- env: {
- ...process.env,
- DEEPSEEK_API_KEY: 'keyless-browser-open-no-call',
- DSH_AGENTS_HOME: join(root, '.agents'),
- DSH_HOME: join(root, '.dsh'),
- DSH_TELEMETRY_DISABLED: '1',
- NODE_NO_WARNINGS: '1',
- SSH_CONNECTION: '',
- SSH_TTY: '',
- },
- input: '',
- timeout: 30_000,
- killSignal: 'SIGKILL',
- reject: false,
- })
- const diagnostic = result.stderr.split(/\r?\n/u)
- .find(line => line.startsWith('Error: dsh: '))
- ?.replace(/^Error: dsh: .*[/\\]\.env/u, 'dsh: {{root}}/.env')
- expect({
- diagnostic,
- exitCode: result.exitCode,
- opening: result.stdout.includes(openingMessage),
- opened: result.stdout.includes('dsh browser-open: '),
- ready: result.stdout.includes('dsh web: '),
- }).toMatchInlineSnapshot(`
- {
- "diagnostic": "dsh: {{root}}/.env sets "BROWSER", which only the launching environment may set (it decides how this process starts, where its code and instructions load from, or how it reaches the network); export BROWSER instead of putting it in a .env file",
- "exitCode": 1,
- "opened": false,
- "opening": false,
- "ready": false,
- }
- `)
- })
- })
|