chore: establish YCode replication baseline and TODO
This commit is contained in:
commit
6dc59f89ba
1118 files changed
+196002
No files matched your search
@@ -0,0 +1,206 @@
|
||||
import { createElement } from 'react'
|
||||
import { renderToStaticMarkup } from 'react-dom/server'
|
||||
import InlineCode, { extractInlineCodeText, isResolvedPathType } from '../src/renderer/src/workbuddy/paths/InlineCode'
|
||||
import {
|
||||
getCachedResolvedType,
|
||||
revalidateAndOpenPath,
|
||||
subscribeToPathResolution,
|
||||
type PathResolutionValue,
|
||||
type PathTypeResolver
|
||||
} from '../src/renderer/src/workbuddy/paths/path-resolution'
|
||||
import {
|
||||
detectPath,
|
||||
extractPurePath,
|
||||
getPathDisplayText,
|
||||
hasLineRange,
|
||||
isAbsolutePath,
|
||||
isFilename,
|
||||
isRelativePath,
|
||||
isSymbol,
|
||||
middleEllipsis,
|
||||
parseLineRange,
|
||||
shouldDetectAsPath,
|
||||
truncatePathDisplay,
|
||||
type PathType
|
||||
} from '../src/renderer/src/workbuddy/paths/path-detection'
|
||||
|
||||
// Expectations are derived from REA-captured path ranges 722350-724687 and inline-code ranges 722904-723374, 727268-728626.
|
||||
let checks = 0
|
||||
|
||||
function check(label: string, condition: boolean): void {
|
||||
if (!condition) throw new Error(label)
|
||||
checks++
|
||||
}
|
||||
|
||||
function detectedType(value: string): PathType | undefined {
|
||||
const detection = detectPath(value)
|
||||
return detection.isPath ? detection.type : undefined
|
||||
}
|
||||
|
||||
function deferred<T>(): { promise: Promise<T>; resolve: (value: T) => void; reject: (reason?: unknown) => void } {
|
||||
let resolve!: (value: T) => void
|
||||
let reject!: (reason?: unknown) => void
|
||||
const promise = new Promise<T>((res, rej) => {
|
||||
resolve = res
|
||||
reject = rej
|
||||
})
|
||||
return { promise, resolve, reject }
|
||||
}
|
||||
|
||||
function nextTurn(): Promise<void> {
|
||||
return new Promise((resolve) => setTimeout(resolve, 0))
|
||||
}
|
||||
|
||||
async function main(): Promise<void> {
|
||||
check('detects Windows absolute paths', isAbsolutePath('C:\\workspace\\src\\main.ts') && detectedType('C:\\workspace\\src\\main.ts') === 'file')
|
||||
check('detects POSIX absolute paths', isAbsolutePath('/workspace/src/main.ts') && detectedType('/workspace/src/main.ts') === 'file')
|
||||
check('detects relative paths and filename-only paths',
|
||||
isRelativePath('src\\renderer\\App.tsx') && isFilename('README.md') && detectedType('README.md') === 'file')
|
||||
check('classifies trailing slash paths as directories',
|
||||
detectedType('/workspace/src/') === 'directory' && detectedType('C:\\workspace\\src\\') === 'directory')
|
||||
|
||||
const ranged = detectPath('C:\\workspace\\src\\main.ts#L12-L28')
|
||||
check('parses #Lstart-Lend and extracts the pure Windows path',
|
||||
ranged.isPath && ranged.type === 'file' && ranged.purePath === 'C:\\workspace\\src\\main.ts' &&
|
||||
ranged.range?.start === 12 && ranged.range?.end === 28 && extractPurePath('src/main.ts#L4') === 'src/main.ts')
|
||||
check('parses single-line ranges with an omitted end',
|
||||
hasLineRange('/workspace/src/main.ts#L7') && parseLineRange('/workspace/src/main.ts#L7')?.start === 7 &&
|
||||
parseLineRange('/workspace/src/main.ts#L7')?.end === undefined)
|
||||
check('does not treat lowercase #l ranges or invalid suffixes as source ranges',
|
||||
!hasLineRange('/workspace/src/main.ts#l7') && !hasLineRange('/workspace/src/main.ts#L0x'))
|
||||
|
||||
check('recognizes symbol identifiers and leaves unrelated strings unclassified',
|
||||
isSymbol('renderMessage_2') && isSymbol('_private') && detectedType('renderMessage') === 'symbol' &&
|
||||
!shouldDetectAsPath('ordinary words'))
|
||||
check('symbols without a resolver result are not resolved path types',
|
||||
!isResolvedPathType(undefined) && !isResolvedPathType('unknown') && isResolvedPathType('symbol'))
|
||||
|
||||
check('middle ellipsis uses balanced retained text and exact length',
|
||||
middleEllipsis('abcdefghijklmnopqrst', 10) === 'abcd...rst')
|
||||
const truncatedPath = truncatePathDisplay('C:\\very\\long\\path\\to\\file.ts', 20)
|
||||
check('path truncation preserves the basename when space allows',
|
||||
truncatedPath === 'C:\\very\\lo...file.ts' && truncatedPath.length === 20)
|
||||
check('path display shows a file basename and keeps its line suffix',
|
||||
getPathDisplayText('C:\\repo\\src\\file.ts#L12-L14', 'C:\\repo\\src\\file.ts', 'file') === 'file.ts#L12-L14')
|
||||
check('path display truncates directory and symbol text',
|
||||
getPathDisplayText('/workspace/a/very/long/path', '/workspace/a/very/long/path', 'directory') ===
|
||||
truncatePathDisplay('/workspace/a/very/long/path') && getPathDisplayText('renderMessage', 'renderMessage', 'symbol') === 'renderMessage')
|
||||
|
||||
const nested = createElement('span', null, 'src/', createElement('code', null, 'App.tsx'), 3)
|
||||
check('extracts code text recursively from React children', extractInlineCodeText(nested) === 'src/App.tsx3')
|
||||
|
||||
let nativeHandlerCalls = 0
|
||||
const ssr = renderToStaticMarkup(createElement(InlineCode, {
|
||||
children: 'C:\\repo\\src\\main.ts#L12-L18',
|
||||
className: 'custom-code',
|
||||
isFinished: true,
|
||||
pathClickHandler: () => { nativeHandlerCalls++ },
|
||||
resolveCode: async () => 'file' as const,
|
||||
requestId: 'request-1',
|
||||
paths: ['C:\\repo\\src\\main.ts']
|
||||
}))
|
||||
check('SSR leaves unresolved inline paths plain and performs no native action',
|
||||
ssr.includes('class="cr-inline-code custom-code"') && ssr.includes('C:\\repo\\src\\main.ts#L12-L18') &&
|
||||
!ssr.includes('cr-clickable-path') && nativeHandlerCalls === 0)
|
||||
|
||||
const streamedCode = 'C:\\repo\\src\\stream.ts'
|
||||
subscribeToPathResolution('stream-request', streamedCode, [], async () => 'file' as const, () => undefined)
|
||||
await nextTurn()
|
||||
let streamedNativeCalls = 0
|
||||
const cachedSsr = renderToStaticMarkup(createElement(InlineCode, {
|
||||
children: streamedCode,
|
||||
isFinished: false,
|
||||
pathClickHandler: () => { streamedNativeCalls++ },
|
||||
resolveCode: async () => 'unknown' as const,
|
||||
requestId: 'stream-request'
|
||||
}))
|
||||
check('does not gate already-resolved inline paths on isFinished and SSR performs no native action',
|
||||
cachedSsr.includes('cr-clickable-path cr-path-type-file') && cachedSsr.includes('title="C:\\repo\\src\\stream.ts"') &&
|
||||
streamedNativeCalls === 0)
|
||||
|
||||
const sharedRequest = deferred<PathResolutionValue>()
|
||||
const sharedCode = 'C:\\repo\\src\\shared.ts'
|
||||
const sharedPaths = ['C:\\repo\\src\\shared.ts']
|
||||
let resolverCalls = 0
|
||||
const firstResults: PathResolutionValue[] = []
|
||||
const secondResults: PathResolutionValue[] = []
|
||||
const resolver: PathTypeResolver = () => {
|
||||
resolverCalls++
|
||||
return sharedRequest.promise
|
||||
}
|
||||
subscribeToPathResolution('cache-request', sharedCode, sharedPaths, resolver, (type) => firstResults.push(type))
|
||||
subscribeToPathResolution('cache-request', sharedCode, [...sharedPaths], resolver, (type) => secondResults.push(type))
|
||||
check('deduplicates simultaneous requests with equal request id, code, and paths', resolverCalls === 1)
|
||||
sharedRequest.resolve('file')
|
||||
await nextTurn()
|
||||
check('notifies all active subscribers and caches the resolved path type',
|
||||
firstResults[0] === 'file' && secondResults[0] === 'file' &&
|
||||
getCachedResolvedType('cache-request', sharedCode, sharedPaths) === 'file')
|
||||
let cacheReplay: PathResolutionValue
|
||||
subscribeToPathResolution('cache-request', sharedCode, sharedPaths, () => {
|
||||
resolverCalls++
|
||||
return Promise.resolve('directory')
|
||||
}, (type) => { cacheReplay = type })
|
||||
check('reuses cached resolution without another resolver call', cacheReplay! === 'file' && resolverCalls === 1)
|
||||
|
||||
const revalidationEvents: string[] = []
|
||||
let revalidationCalls = 0
|
||||
const revalidatedType = await revalidateAndOpenPath(
|
||||
'cache-request',
|
||||
sharedCode,
|
||||
sharedPaths,
|
||||
async () => {
|
||||
revalidationCalls++
|
||||
return 'directory' as const
|
||||
},
|
||||
(type) => revalidationEvents.push(`state:${type}`),
|
||||
(type) => revalidationEvents.push(`open:${type}`)
|
||||
)
|
||||
check('click revalidation bypasses the mount cache, updates state, then opens the refreshed type',
|
||||
revalidatedType === 'directory' && revalidationCalls === 1 &&
|
||||
getCachedResolvedType('cache-request', sharedCode, sharedPaths) === 'directory' &&
|
||||
revalidationEvents.join(',') === 'state:directory,open:directory')
|
||||
|
||||
const failedRevalidationEvents: string[] = []
|
||||
const failedRevalidation = await revalidateAndOpenPath(
|
||||
'cache-request',
|
||||
sharedCode,
|
||||
sharedPaths,
|
||||
async () => { throw new Error('path changed before opening') },
|
||||
(type) => failedRevalidationEvents.push(`state:${type}`),
|
||||
(type) => failedRevalidationEvents.push(`open:${type}`)
|
||||
)
|
||||
check('failed click revalidation caches unknown and does not open the stale path',
|
||||
failedRevalidation === 'unknown' && getCachedResolvedType('cache-request', sharedCode, sharedPaths) === 'unknown' &&
|
||||
failedRevalidationEvents.join(',') === 'state:unknown')
|
||||
|
||||
const cancelledRequest = deferred<PathResolutionValue>()
|
||||
const cancelledResults: PathResolutionValue[] = []
|
||||
const cancel = subscribeToPathResolution('cancel-request', '/repo/src/item.ts', [], () => cancelledRequest.promise, (type) => cancelledResults.push(type))
|
||||
cancel()
|
||||
cancelledRequest.resolve('directory')
|
||||
await nextTurn()
|
||||
check('cancels stale component updates without discarding the shared result',
|
||||
cancelledResults.length === 0 && getCachedResolvedType('cancel-request', '/repo/src/item.ts', []) === 'directory')
|
||||
|
||||
let rejectedCalls = 0
|
||||
const rejectedResults: PathResolutionValue[] = []
|
||||
const rejectResolver: PathTypeResolver = () => {
|
||||
rejectedCalls++
|
||||
return Promise.reject(new Error('expected resolver failure'))
|
||||
}
|
||||
subscribeToPathResolution('error-request', 'symbolName', [], rejectResolver, (type) => rejectedResults.push(type))
|
||||
await nextTurn()
|
||||
let rejectedCacheReplay: PathResolutionValue
|
||||
subscribeToPathResolution('error-request', 'symbolName', [], () => {
|
||||
rejectedCalls++
|
||||
return Promise.resolve('symbol')
|
||||
}, (type) => { rejectedCacheReplay = type })
|
||||
check('caches resolver failures as unknown and suppresses repeat requests',
|
||||
rejectedResults[0] === 'unknown' && rejectedCacheReplay! === 'unknown' && rejectedCalls === 1 &&
|
||||
!isResolvedPathType(rejectedCacheReplay))
|
||||
|
||||
console.log(`Path renderer regression checks passed (${checks}).`)
|
||||
}
|
||||
|
||||
void main()
|
||||
Reference in new issue
Block a user