feat: publish CodexPi contest snapshot
Some checks are pending
Qualité / tests (push) Waiting to run

This commit is contained in:
CodexPi
2026-11-14 20:23:59 +01:00
commit 995b17a6e2
295 changed files with 36803 additions and 0 deletions

View File

@@ -0,0 +1,15 @@
import assert from 'node:assert/strict'
import {readFile} from 'node:fs/promises'
import test from 'node:test'
import {csrfTokenFromBootstrap} from '../src/csrfRefresh.ts'
test('a fresh CSRF token is required before submitting login', async () => {
assert.equal(csrfTokenFromBootstrap({csrf_token: 'fresh-token'}), 'fresh-token')
assert.throws(() => csrfTokenFromBootstrap({}), /indisponible/i)
const app = await readFile(new URL('../src/App.tsx', import.meta.url), 'utf8')
const refresh = app.indexOf('await refreshCsrf()')
const login = app.indexOf("const endpoint=setupRequired?'/api/auth/setup':'/api/auth/login'")
assert.ok(refresh >= 0 && refresh < login, 'login must refresh CSRF before selecting the POST endpoint')
})

View File

@@ -0,0 +1,22 @@
import assert from 'node:assert/strict'
import test from 'node:test'
import {expectArrayPayload, readJsonPayload} from '../src/apiPayload.ts'
test('a truncated successful JSON response is rejected instead of becoming an object', async () => {
const response = new Response('{"messages":[', {
status: 200,
headers: {'Content-Type': 'application/json'},
})
await assert.rejects(() => readJsonPayload(response), /incomplète/i)
})
test('a valid JSON response is decoded', async () => {
const response = new Response('[{"id":"message-1"}]', {status: 200})
assert.deepEqual(await readJsonPayload(response), [{id: 'message-1'}])
})
test('array endpoints reject a valid but malformed payload', () => {
assert.throws(() => expectArrayPayload({}, 'messages'), /messages.*invalide/i)
assert.deepEqual(expectArrayPayload([{id: 'message-1'}], 'messages'), [{id: 'message-1'}])
})

View File

@@ -0,0 +1,18 @@
import assert from 'node:assert/strict'
import test from 'node:test'
import { routeHref, routeView } from '../src/appRoutes.ts'
test('deep links restore the requested view',()=>{
assert.equal(routeView('?view=activity'),'activity')
assert.equal(routeView('?view=workspace&project=p1&conversation=c1'),'workspace')
assert.equal(routeView('?view=unknown'),'company')
})
test('workspace links retain their project, conversation and tab',()=>{
assert.equal(routeHref('workspace',{project:{id:'p1'},conversationId:'c1',tab:'changes'}),'/?view=workspace&project=p1&conversation=c1&tab=changes')
})
test('global machine links do not depend on a selected project',()=>{
assert.equal(routeHref('remote',{project:{id:'p1'}}),'/?view=remote')
})

View File

@@ -0,0 +1,25 @@
import assert from 'node:assert/strict'
import test from 'node:test'
import {interfaceLanding,rememberAppearance,storedInterfaceType,storedTheme} from '../src/appearance.ts'
function memory(initial:Record<string,string>={}){
const values=new Map(Object.entries(initial))
return{getItem:(key:string)=>values.get(key)??null,setItem:(key:string,value:string)=>values.set(key,value),values}
}
test('each interface type owns a distinct landing structure',()=>{
assert.deepEqual(interfaceLanding('dashboard'),{view:'activity',tab:'chat'})
assert.deepEqual(interfaceLanding('atelier'),{view:'workspace',tab:'chat'})
assert.deepEqual(interfaceLanding('review'),{view:'workspace',tab:'changes'})
})
test('interface and theme persist independently with safe defaults',()=>{
const storage=memory()
rememberAppearance('review','light',storage)
assert.equal(storedInterfaceType(storage),'review')
assert.equal(storedTheme(storage),'light')
const invalid=memory({'codexpi-interface-type':'palette','codexpi-theme':'neon'})
assert.equal(storedInterfaceType(invalid),'dashboard')
assert.equal(storedTheme(invalid),'system')
})

View File

@@ -0,0 +1,46 @@
import assert from 'node:assert/strict'
import test from 'node:test'
import {
MAX_ATTACHMENT_BYTES,
MAX_COMPOSER_TEXT_LENGTH,
MAX_EDITABLE_TEXT_BYTES,
prepareComposerSubmission,
} from '../src/attachments.ts'
test('files up to one hundred megabytes are accepted', () => {
assert.equal(MAX_ATTACHMENT_BYTES, 100 * 1024 * 1024)
})
test('regular composer text remains inline', () => {
const result = prepareComposerSubmission('short request', [], new Date('2026-09-18T12:34:56Z'))
assert.equal(result.content, 'short request')
assert.deepEqual(result.files, [])
assert.equal(result.converted, false)
})
test('oversized composer text becomes a named text attachment', async () => {
const content = 'x'.repeat(MAX_COMPOSER_TEXT_LENGTH + 1)
const result = prepareComposerSubmission(content, [], new Date('2026-09-18T12:34:56Z'))
assert.equal(result.converted, true)
assert.equal(result.files.length, 1)
assert.equal(result.files[0].name, 'message-long-2026-09-18-123456.txt')
assert.equal(result.files[0].type, 'text/plain')
assert.equal(await result.files[0].text(), content)
assert.match(result.content, /message-long-2026-09-18-123456\.txt/)
})
test('conversion refuses to exceed the four attachment limit', () => {
const files = Array.from({length:4}, (_, index) => new File(['x'], `file-${index}.txt`, {type:'text/plain'}))
assert.throws(
() => prepareComposerSubmission('x'.repeat(MAX_COMPOSER_TEXT_LENGTH + 1), files, new Date()),
/quatre fichiers/i,
)
})
test('conversion refuses text too large for the integrated editor', () => {
assert.throws(
() => prepareComposerSubmission('x'.repeat(MAX_EDITABLE_TEXT_BYTES + 1), [], new Date()),
/10 Mo maximum/i,
)
})

View File

@@ -0,0 +1,18 @@
import assert from 'node:assert/strict'
import test from 'node:test'
import { relativePointerPosition } from '../public/browser-touchpad-math.js'
test('trackpad movement is relative to the current pointer position',()=>{
assert.deepEqual(
relativePointerPosition({x:320,y:180},40,-20,2,2,1.35,1440,900),
{x:428,y:126},
)
})
test('relative trackpad movement stays inside the remote screen',()=>{
assert.deepEqual(
relativePointerPosition({x:1430,y:5},100,-100,1,1,1.35,1440,900),
{x:1439,y:0},
)
})

View File

@@ -0,0 +1,20 @@
import assert from 'node:assert/strict'
import test from 'node:test'
import { parseCommandLine } from '../src/commandLine.ts'
test('quoted arguments retain their spaces',()=>{
assert.deepEqual(parseCommandLine('pytest "tests/my feature.py" -k \'works here\''),['pytest','tests/my feature.py','-k','works here'])
})
test('escaped spaces and empty quoted arguments are retained',()=>{
assert.deepEqual(parseCommandLine('tool one\\ two ""'),['tool','one two',''])
})
test('windows paths are not damaged by parsing',()=>{
assert.deepEqual(parseCommandLine('py "C:\\Mes Tests\\test.py"'),['py','C:\\Mes Tests\\test.py'])
})
test('an unclosed quote is rejected',()=>{
assert.throws(()=>parseCommandLine('pytest "tests/file.py'),/Guillemet non fermé/)
})

View File

@@ -0,0 +1,22 @@
import assert from 'node:assert/strict'
import test from 'node:test'
import {isCurrentConversationResponse,reuseInFlightRequest} from '../src/conversationRequests.ts'
test('a late response from the previously selected conversation is ignored', () => {
assert.equal(isCurrentConversationResponse('gameflix','codexpi-self'),false)
assert.equal(isCurrentConversationResponse('gameflix','gameflix'),true)
})
test('simultaneous refreshes reuse one request and release it after completion', async () => {
const requests=new Map<string,Promise<number>>()
let calls=0
const load=()=>reuseInFlightRequest(requests,'conversation',async()=>++calls)
const first=load()
const second=load()
assert.equal(first,second)
assert.equal(await first,1)
assert.equal(await load(),2)
})

View File

@@ -0,0 +1,17 @@
import assert from 'node:assert/strict'
import {readFileSync} from 'node:fs'
import test from 'node:test'
const common=readFileSync(new URL('../src/Common.tsx',import.meta.url),'utf8')
const styles=readFileSync(new URL('../src/styles.css',import.meta.url),'utf8')
test('inline chat images load eagerly inside the iOS scroll container',()=>{
assert.match(common,/className="markdown-image"[\s\S]*loading="eager"/)
assert.doesNotMatch(common,/className="markdown-image"[\s\S]*loading="lazy"/)
})
test('inline chat images reserve a visible panoramic area before decoding',()=>{
const rule=styles.match(/\.markdown-image>img\{([^}]*)\}/)?.[1]||''
assert.match(rule,/aspect-ratio:auto 16\/9/)
assert.match(rule,/min-height:160px/)
})

View File

@@ -0,0 +1,80 @@
import assert from 'node:assert/strict'
import test from 'node:test'
import {embeddedAttachmentIds, resolveMarkdownImage, resolveMarkdownLink, stableAttachments} from '../src/markdownImages.ts'
const project={id:'project-1',name:'Demo',path:'/home/config/Demo',full_access:0}
const attachments=[
{id:'shot-1',original_name:'capture navigateur.png',content_type:'image/png',size:42,url:'/api/browser/screenshots/shot-1'},
]
test('messages without attachments reuse one stable empty collection',()=>{
assert.equal(stableAttachments(undefined),stableAttachments(undefined))
})
test('an attachment reference is resolved inside markdown text',()=>{
assert.deepEqual(resolveMarkdownImage('attachment://shot-1',attachments,project),{
url:'/api/browser/screenshots/shot-1',
name:'capture navigateur.png',
attachmentId:'shot-1',
reference:'attachment://shot-1',
})
assert.deepEqual(
embeddedAttachmentIds('Avant\n\n![Résultat](attachment://shot-1)\n\nAprès',attachments),
new Set(['shot-1']),
)
})
test('an attachment can be referenced by its encoded file name',()=>{
assert.equal(
resolveMarkdownImage('attachment://capture%20navigateur.png',attachments,project)?.url,
'/api/browser/screenshots/shot-1',
)
})
test('an absolute project image is served through the authenticated download route',()=>{
const image=resolveMarkdownImage('/home/config/Demo/docs/schema.png',[],project)
assert.equal(image?.url,'/api/projects/project-1/files/download?path=docs%2Fschema.png&inline=true')
assert.equal(image?.name,'schema.png')
})
test('an image created in a conversation worktree uses its authorized workspace route',()=>{
const conversation={id:'conversation-1',title:'Images',status:'idle' as const,updated_at:'',worktree_path:'/app/data/worktrees/conversation-1'}
const image=resolveMarkdownImage('/app/data/worktrees/conversation-1/output/result.webp',[],project,conversation)
assert.equal(image?.url,'/api/conversations/conversation-1/files/image?path=output%2Fresult.webp')
})
test('external images remain external and unrelated absolute paths are rejected',()=>{
assert.equal(resolveMarkdownImage('https://example.com/image.png',[],project)?.url,'https://example.com/image.png')
assert.equal(resolveMarkdownImage('/etc/passwd',[],project),null)
})
test('a project archive link becomes an authenticated download opened outside the PWA',()=>{
const link=resolveMarkdownLink('/home/config/Demo/output/boards.zip',project)
assert.deepEqual(link,{
url:'/api/projects/project-1/files/download?path=output%2Fboards.zip',
name:'boards.zip',
download:true,
external:false,
presentation:'dialog',
})
})
test('a relative artifact created in a conversation uses its private worktree route',()=>{
const conversation={id:'conversation-1',title:'Images',status:'idle' as const,updated_at:'',worktree_path:'/app/data/worktrees/conversation-1'}
const link=resolveMarkdownLink('output/boards.zip',project,conversation)
assert.equal(link.url,'/api/conversations/conversation-1/files/download?path=output%2Fboards.zip')
assert.equal(link.name,'boards.zip')
assert.equal(link.download,true)
assert.equal(link.presentation,'dialog')
})
test('external links are opened separately without pretending to be local downloads',()=>{
assert.deepEqual(resolveMarkdownLink('https://example.com/help',project),{
url:'https://example.com/help',name:'help',download:false,external:true,presentation:'external',
})
})
test('ordinary links keep navigating inside the application',()=>{
assert.equal(resolveMarkdownLink('#details',project).presentation,'navigate')
})

View File

@@ -0,0 +1,23 @@
import assert from 'node:assert/strict'
import test from 'node:test'
import {retryPayload} from '../src/retryMessage.ts'
test('retry preserves the original message and attachment identifiers', () => {
const payload=retryPayload({
id:'message-1',
role:'user',
content:'Analyse cette capture',
metadata:{attachments:[
{id:'attachment-1',original_name:'capture.png',content_type:'image/png',size:42,url:'/attachment-1'},
]},
})
assert.deepEqual(payload,{content:'Analyse cette capture',attachment_ids:['attachment-1']})
})
test('retry keeps a text-only message valid', () => {
assert.deepEqual(retryPayload({id:'message-2',role:'user',content:'Réessaie',metadata:{}}),{
content:'Réessaie',
attachment_ids:[],
})
})

View File

@@ -0,0 +1,26 @@
import assert from 'node:assert/strict'
import test from 'node:test'
import { SLASH_COMMANDS, filterSlashCommands, slashCommandValue } from '../src/slashCommands.ts'
test('a lone slash exposes the available command catalog', () => {
assert.ok(SLASH_COMMANDS.length >= 8)
assert.deepEqual(filterSlashCommands('/'), SLASH_COMMANDS)
})
test('a command prefix filters names case-insensitively', () => {
assert.deepEqual(filterSlashCommands('/plu').map(item => item.name), ['/plugins'])
assert.deepEqual(filterSlashCommands('/PLU').map(item => item.name), ['/plugins'])
})
test('suggestions only appear for a single command token at the start', () => {
assert.deepEqual(filterSlashCommands('bonjour /'), [])
assert.deepEqual(filterSlashCommands('/plugins maintenant'), [])
assert.deepEqual(filterSlashCommands('/plugins\ntexte'), [])
})
test('selecting a command leaves a space ready for optional arguments', () => {
const plugins = SLASH_COMMANDS.find(item => item.name === '/plugins')
assert.ok(plugins)
assert.equal(slashCommandValue(plugins), '/plugins ')
})

View File

@@ -0,0 +1,35 @@
import assert from 'node:assert/strict'
import test from 'node:test'
import { speechTextFromMarkdown, splitSpeechText } from '../src/speech.ts'
test('markdown is converted to natural speech without fenced code or URLs', () => {
const markdown = `# Résultat
Voici **la réponse** avec [la documentation](https://example.com/docs) et \`npm test\`.
\`\`\`ts
const secret = 'not read aloud'
\`\`\`
- Premier point
- Deuxième point`
const spoken = speechTextFromMarkdown(markdown)
assert.match(spoken, /Résultat/)
assert.match(spoken, /la réponse/)
assert.match(spoken, /la documentation/)
assert.match(spoken, /npm test/)
assert.match(spoken, /Bloc de code omis/)
assert.doesNotMatch(spoken, /https|secret|\*\*|```/)
})
test('long speech is split into short non-empty chunks without losing text', () => {
const text = `${'Une phrase assez longue. '.repeat(18)}Dernière phrase.`
const chunks = splitSpeechText(text, 180)
assert.ok(chunks.length > 1)
assert.ok(chunks.every(chunk => chunk.length > 0 && chunk.length <= 180))
assert.equal(chunks.join(' '), text.replace(/\s+/g, ' ').trim())
})