chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,213 @@
|
||||
const {
|
||||
test: it, expect
|
||||
} = require('@playwright/test')
|
||||
const { describe } = it
|
||||
const { listWidgets, runWidget, stopWidget } = require('../../src/app/widgets/load-widget')
|
||||
const os = require('os')
|
||||
const path = require('path')
|
||||
const fs = require('fs/promises')
|
||||
const axios = require('axios')
|
||||
it.setTimeout(100000)
|
||||
|
||||
describe('load-widget', function () {
|
||||
let serverInstance = null
|
||||
let testDir = null
|
||||
let serverUrl = null
|
||||
|
||||
// Helper to create test files
|
||||
async function createTestFiles () {
|
||||
testDir = path.join(os.tmpdir(), 'electerm-test-' + Date.now())
|
||||
await fs.mkdir(testDir)
|
||||
|
||||
// Create a test index.html
|
||||
const htmlContent = `
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>
|
||||
Test Page
|
||||
|
||||
</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>
|
||||
Hello from Electerm Test
|
||||
|
||||
</h1>
|
||||
<p>
|
||||
This is a test page served by local-file-server widget.
|
||||
|
||||
</p>
|
||||
<p>
|
||||
Timestamp: ${new Date().toISOString()}
|
||||
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
`
|
||||
await fs.writeFile(path.join(testDir, 'index.html'), htmlContent)
|
||||
}
|
||||
|
||||
// Cleanup helper
|
||||
async function cleanup () {
|
||||
if (serverInstance) {
|
||||
await stopWidget(serverInstance)
|
||||
serverInstance = null
|
||||
}
|
||||
if (testDir) {
|
||||
try {
|
||||
await fs.rm(testDir, { recursive: true, force: true })
|
||||
} catch (err) {
|
||||
console.error('Cleanup error:', err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Setup before tests
|
||||
it.beforeEach(async () => {
|
||||
await createTestFiles()
|
||||
})
|
||||
|
||||
// Cleanup after tests
|
||||
it.afterEach(async () => {
|
||||
await cleanup()
|
||||
})
|
||||
|
||||
// Test listWidgets function
|
||||
it('listWidgets should list available widgets', async function () {
|
||||
const widgets = listWidgets()
|
||||
console.log(widgets)
|
||||
expect(Array.isArray(widgets)).toBe(true)
|
||||
expect(widgets.length).toBeGreaterThan(0)
|
||||
|
||||
const fileServer = widgets.find(w => w.id === 'local-file-server')
|
||||
expect(fileServer).toBeTruthy()
|
||||
})
|
||||
|
||||
// Test runWidget function with file serving
|
||||
it('should run local-file-server widget and serve files', async function () {
|
||||
const config = {
|
||||
directory: testDir,
|
||||
port: 3457,
|
||||
host: '127.0.0.1',
|
||||
maxAge: 365 * 24 * 60 * 60 * 1000,
|
||||
cacheControl: true,
|
||||
lastModified: true,
|
||||
etag: true,
|
||||
dotfiles: 'allow',
|
||||
redirect: true,
|
||||
acceptRanges: true,
|
||||
index: 'index.html'
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await runWidget('local-file-server', config)
|
||||
expect(result).toBeTruthy()
|
||||
expect(result.instanceId).toBeTruthy()
|
||||
expect(result.serverInfo).toBeTruthy()
|
||||
expect(result.serverInfo.url).toBe(`http://${config.host}:${config.port}`)
|
||||
expect(result.serverInfo.path).toBe(config.directory)
|
||||
|
||||
serverInstance = result.instanceId
|
||||
serverUrl = result.serverInfo.url
|
||||
|
||||
// Test file serving
|
||||
const response = await axios.get(serverUrl)
|
||||
expect(response.status).toBe(200)
|
||||
expect(response.headers['content-type']).toMatch(/text\/html/)
|
||||
expect(response.data).toContain('Hello from Electerm Test')
|
||||
|
||||
// Test caching headers
|
||||
expect(response.headers['cache-control']).toBeTruthy()
|
||||
expect(response.headers.etag).toBeTruthy()
|
||||
expect(response.headers['last-modified']).toBeTruthy()
|
||||
|
||||
// Test non-existent file
|
||||
try {
|
||||
await axios.get(`${serverUrl}/non-existent.html`)
|
||||
throw new Error('Should have thrown 404')
|
||||
} catch (err) {
|
||||
expect(err.response.status).toBe(404)
|
||||
}
|
||||
} catch (err) {
|
||||
console.log('run widget error:', err)
|
||||
throw err
|
||||
}
|
||||
})
|
||||
|
||||
// Test directory listing (if enabled)
|
||||
it('should handle directory requests properly', async function () {
|
||||
if (!serverInstance || !serverUrl) {
|
||||
const config = {
|
||||
directory: testDir,
|
||||
port: 3457,
|
||||
host: '127.0.0.1',
|
||||
maxAge: 365 * 24 * 60 * 60 * 1000,
|
||||
cacheControl: true,
|
||||
lastModified: true,
|
||||
etag: true,
|
||||
dotfiles: 'allow',
|
||||
redirect: true,
|
||||
acceptRanges: true,
|
||||
index: 'index.html'
|
||||
}
|
||||
const result = await runWidget('local-file-server', config)
|
||||
serverInstance = result.instanceId
|
||||
serverUrl = result.serverInfo.url
|
||||
}
|
||||
|
||||
// Create a subdirectory with a file
|
||||
const subDir = path.join(testDir, 'subdir')
|
||||
await fs.mkdir(subDir)
|
||||
await fs.writeFile(path.join(subDir, 'index.html'), 'Test content')
|
||||
|
||||
// Test directory redirect (should redirect to index.html)
|
||||
const response = await axios.get(`${serverUrl}/subdir/`)
|
||||
expect(response.status).toBe(200)
|
||||
})
|
||||
|
||||
// Test stopWidget function
|
||||
it('should stop running widget', async function () {
|
||||
if (!serverInstance) {
|
||||
console.log('No server instance to stop')
|
||||
return
|
||||
}
|
||||
|
||||
const result = await stopWidget(serverInstance)
|
||||
expect(result).toBeTruthy()
|
||||
expect(result.instanceId).toBe(serverInstance)
|
||||
expect(result.status).toBe('stopped')
|
||||
|
||||
// Verify server is actually stopped
|
||||
try {
|
||||
await axios.get(serverUrl)
|
||||
throw new Error('Server should be stopped')
|
||||
} catch (err) {
|
||||
expect(err.code).toBe('ECONNREFUSED')
|
||||
}
|
||||
})
|
||||
|
||||
// Test error cases
|
||||
it('should handle stopping non-existent widget', async function () {
|
||||
const result = await stopWidget('non-existent-id')
|
||||
expect(result).toBeUndefined()
|
||||
})
|
||||
|
||||
it('should handle invalid widget ID', async function () {
|
||||
try {
|
||||
await runWidget('non-existent-widget', {})
|
||||
throw new Error('Should have thrown an error')
|
||||
} catch (error) {
|
||||
expect(error).toBeTruthy()
|
||||
}
|
||||
})
|
||||
|
||||
it('should reject traversed widget IDs', async function () {
|
||||
try {
|
||||
await runWidget('../../../../../tmp/evil', {})
|
||||
throw new Error('Should have thrown an error')
|
||||
} catch (error) {
|
||||
expect(error.message).toContain('Invalid widget ID')
|
||||
}
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user