-
Notifications
You must be signed in to change notification settings - Fork 1
Test webview messages and server lifecycle #146
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: codex/issue-132-passing-min-vscode
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,117 @@ | ||
| import * as assert from 'node:assert/strict'; | ||
| import * as path from 'node:path'; | ||
| import * as vscode from 'vscode'; | ||
| import type { LJDiagnostic } from '../types/diagnostics'; | ||
| import type { LJContext } from '../types/context'; | ||
| import type { LiquidJavaTestApi } from '../types/test-api'; | ||
|
|
||
| suite('Bundled LiquidJava webview and lifecycle', () => { | ||
| test('sends verification messages and verifies again after Stop, Start, and Restart', async () => { | ||
| const installed = vscode.extensions.getExtension<LiquidJavaTestApi>('AlcidesFonseca.liquid-java'); | ||
| assert.ok(installed); | ||
| const api = await installed.activate(); | ||
| await api.ready; | ||
|
|
||
| const workspace = vscode.workspace.workspaceFolders?.[0]; | ||
| assert.ok(workspace); | ||
| const uri = vscode.Uri.joinPath(workspace.uri, 'src/main/java/FailingRefinement.java'); | ||
| const sameFile = (file: string) => vscode.Uri.file(path.resolve(file)).fsPath === uri.fsPath; | ||
| const subscriptions: vscode.Disposable[] = []; | ||
| const nextEvent = <T>(event: vscode.Event<T>, matches: (value: T) => boolean) => | ||
| new Promise<T>(resolve => { | ||
| const subscription = event(value => { | ||
| if (matches(value)) { | ||
| subscription.dispose(); | ||
| resolve(value); | ||
| } | ||
| }); | ||
| subscriptions.push(subscription); | ||
| }); | ||
| const isFixtureDiagnostic = (diagnostics: LJDiagnostic[]) => diagnostics.some(diagnostic => | ||
| diagnostic.type === 'refinement-error' && sameFile(diagnostic.file)); | ||
| const nextDiagnostics = () => nextEvent(api.onDiagnostics, isFixtureDiagnostic); | ||
| const assertServerRunning = () => { | ||
| const pid = api.getState().serverPid; | ||
| assert.ok(pid, 'the bundled extension must expose its running Java server'); | ||
| assert.doesNotThrow(() => process.kill(pid, 0)); | ||
| return pid; | ||
| }; | ||
| const assertServerStopped = (pid: number) => { | ||
| assert.equal(api.getState().serverPid, undefined); | ||
| assert.throws(() => process.kill(pid, 0), { code: 'ESRCH' }, 'Stop must terminate the Java process'); | ||
| assert.equal(api.getState().status, 'stopped'); | ||
| assert.deepEqual(api.getState().diagnostics, []); | ||
| }; | ||
|
|
||
| try { | ||
| await vscode.commands.executeCommand('workbench.action.closeSidebar'); | ||
| await vscode.commands.executeCommand('workbench.action.closeAllEditors'); | ||
| await vscode.commands.executeCommand('liquidjava.stop'); | ||
| await vscode.commands.executeCommand('liquidjava.start'); | ||
| const ready = nextEvent(api.onWebviewMessage, event => | ||
| event.direction === 'fromWebview' && event.message.type === 'ready'); | ||
| await vscode.commands.executeCommand('liquidjava.showView'); | ||
| await ready; | ||
|
|
||
| const initialDiagnostics = nextDiagnostics(); | ||
| const document = await vscode.workspace.openTextDocument(uri); | ||
| await vscode.window.showTextDocument(document); | ||
| await initialDiagnostics; | ||
|
|
||
| const diagnosticMessage = nextEvent(api.onWebviewMessage, event => | ||
| event.direction === 'toWebview' && event.message.type === 'diagnostics' && | ||
| isFixtureDiagnostic(event.message.diagnostics)); | ||
| const contextMessage = nextEvent(api.onWebviewMessage, event => | ||
| event.direction === 'toWebview' && event.message.type === 'context'); | ||
| const manualDiagnostics = nextDiagnostics(); | ||
| await vscode.commands.executeCommand('liquidjava.verify'); | ||
| assert.equal(api.getState().status, 'loading'); | ||
| const [diagnostics, outboundDiagnostics, outboundContext] = await Promise.all([ | ||
| manualDiagnostics, diagnosticMessage, contextMessage, | ||
| ]); | ||
| assert.deepEqual(outboundDiagnostics.message.diagnostics, diagnostics); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This compares an array with itself. |
||
| const context: LJContext = outboundContext.message.context; | ||
| const valid = context.localVars.find(variable => variable.name === 'valid'); | ||
| assert.ok(valid, JSON.stringify(context)); | ||
| assert.ok(valid.position); | ||
| assert.ok(sameFile(valid.position.file)); | ||
| assert.equal(valid.type, 'int'); | ||
| assert.ok(valid.mainRefinement.includes('> 0')); | ||
| assert.equal(api.getState().status, 'failed'); | ||
|
|
||
| const originalPid = assertServerRunning(); | ||
| const stopped = nextEvent(api.onWebviewMessage, event => | ||
| event.direction === 'toWebview' && event.message.type === 'status' && event.message.status === 'stopped'); | ||
| await vscode.commands.executeCommand('liquidjava.stop'); | ||
| await stopped; | ||
| assertServerStopped(originalPid); | ||
|
|
||
| const startDiagnostics = nextDiagnostics(); | ||
| await vscode.commands.executeCommand('liquidjava.start'); | ||
| await startDiagnostics; | ||
| const startedPid = assertServerRunning(); | ||
| assert.notEqual(startedPid, originalPid); | ||
| assert.equal(api.getState().status, 'failed'); | ||
|
|
||
| const restartStopped = nextEvent(api.onWebviewMessage, event => | ||
| event.direction === 'toWebview' && event.message.type === 'status' && event.message.status === 'stopped'); | ||
| const restartDiagnostics = nextDiagnostics(); | ||
| await vscode.commands.executeCommand('liquidjava.restart'); | ||
| await restartStopped; | ||
| await restartDiagnostics; | ||
| const restartedPid = assertServerRunning(); | ||
| assert.notEqual(restartedPid, startedPid); | ||
| assert.throws(() => process.kill(startedPid, 0), { code: 'ESRCH' }); | ||
|
|
||
| const finalDiagnostics = nextDiagnostics(); | ||
| await vscode.commands.executeCommand('liquidjava.verify'); | ||
| await finalDiagnostics; | ||
| assert.equal(api.getState().status, 'failed'); | ||
| } finally { | ||
| subscriptions.forEach(subscription => subscription.dispose()); | ||
| await vscode.commands.executeCommand('workbench.action.closeAllEditors'); | ||
| await vscode.commands.executeCommand('liquidjava.stop'); | ||
| await vscode.commands.executeCommand('liquidjava.start'); | ||
| } | ||
| }); | ||
| }); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,11 @@ | ||
| import liquidjava.specification.Refinement; | ||
|
|
||
| public class FailingRefinement { | ||
| @Refinement("_ > 0") | ||
| int positive = -1; | ||
| void check() { | ||
| @Refinement("_ > 0") | ||
| int valid = 1; | ||
|
|
||
| @Refinement("_ > 0") | ||
| int positive = -1; | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can match the old context.
handleLJDiagnosticssends acontextmessage with the cachedextension.contextfrom the previous verification, so this listener can resolve on that message, not on the context Verify produces. The fixture doesn't change between runs, so the old and new contexts look the same, and the assertions onvalidpass even if Verify stopped sending a context.Suggest waiting for the
contextmessage that follows theliquidjava/contextnotification for this run, for example by clearing the cached context before Verify, or by matching on something that changes per run.