55
66import * as vscode from 'vscode' ;
77import * as path from 'path' ;
8+ import * as assert from 'assert' ;
89
910export let doc : vscode . TextDocument ;
1011export let editor : vscode . TextEditor ;
1112export let documentEol : string ;
1213export let platformEol : string ;
1314
1415/**
15- * Activates the vscode.lsp-sample extension
16+ * Tests activate the extension through a real editor open so the language
17+ * server lifecycle matches what users do in VS Code.
1618 */
1719export async function activate ( docUri : vscode . Uri ) {
18- // The extensionId is `publisher.name` from package.json
1920 const ext = vscode . extensions . getExtension ( 'stackpress.idea-schema' ) ! ;
2021 await ext . activate ( ) ;
2122 try {
2223 doc = await vscode . workspace . openTextDocument ( docUri ) ;
2324 editor = await vscode . window . showTextDocument ( doc ) ;
24- await sleep ( 2000 ) ; // Wait for server activation
25+ // A short delay keeps the tests black-box oriented by waiting for the
26+ // server the same way a user would, instead of poking internal events.
27+ await sleep ( 2000 ) ;
2528 } catch ( e ) {
2629 console . error ( e ) ;
2730 }
2831}
2932
33+ /**
34+ * Polling-based waits are good enough here because the integration suite is
35+ * validating editor-visible behavior rather than micro-benchmarking latency.
36+ */
3037async function sleep ( ms : number ) {
3138 return new Promise ( resolve => setTimeout ( resolve , ms ) ) ;
3239}
3340
41+ /**
42+ * Fixtures live outside the compiled `out` directory so path resolution is
43+ * anchored relative to the test source layout.
44+ */
3445export const getDocPath = ( p : string ) => {
3546 return path . resolve ( __dirname , '../../testFixture' , p ) ;
3647} ;
48+
3749export const getDocUri = ( p : string ) => {
3850 return vscode . Uri . file ( getDocPath ( p ) ) ;
3951} ;
4052
53+ /**
54+ * Replacing the full document keeps completion/definition tests compact and
55+ * avoids creating a new fixture file for every single editing scenario.
56+ */
4157export async function setTestContent ( content : string ) : Promise < boolean > {
4258 const all = new vscode . Range (
4359 doc . positionAt ( 0 ) ,
4460 doc . positionAt ( doc . getText ( ) . length )
4561 ) ;
46- return editor . edit ( eb => eb . replace ( all , content ) ) ;
47- }
62+ const applied = await editor . edit ( eb => eb . replace ( all , content ) ) ;
63+ // The follow-up pause lets the LSP server digest the edit before the test
64+ // asks for completions or diagnostics.
65+ await sleep ( 300 ) ;
66+ return applied ;
67+ }
68+
69+ /**
70+ * Diagnostics are asynchronous, so tests wait until the expected minimum
71+ * appears instead of assuming the server responded immediately.
72+ */
73+ export async function waitForDiagnostics ( uri : vscode . Uri , minimum = 1 ) {
74+ for ( let attempt = 0 ; attempt < 20 ; attempt ++ ) {
75+ const diagnostics = vscode . languages . getDiagnostics ( uri ) ;
76+ if ( diagnostics . length >= minimum ) {
77+ return diagnostics ;
78+ }
79+ await sleep ( 200 ) ;
80+ }
81+ return vscode . languages . getDiagnostics ( uri ) ;
82+ }
83+
84+ /**
85+ * Some completion tests temporarily introduce incomplete text, so this helper
86+ * waits until the document has settled back into a clean state first.
87+ */
88+ export async function waitForNoDiagnostics ( uri : vscode . Uri ) {
89+ for ( let attempt = 0 ; attempt < 20 ; attempt ++ ) {
90+ const diagnostics = vscode . languages . getDiagnostics ( uri ) ;
91+ if ( diagnostics . length === 0 ) {
92+ return diagnostics ;
93+ }
94+ await sleep ( 200 ) ;
95+ }
96+ return vscode . languages . getDiagnostics ( uri ) ;
97+ }
98+
99+ /**
100+ * Tests only care about the visible labels because the user-facing list is
101+ * what confirms the context classifier is doing the right thing.
102+ */
103+ export async function getCompletionLabels ( uri : vscode . Uri , position : vscode . Position ) {
104+ const completionList = await vscode . commands . executeCommand < vscode . CompletionList > (
105+ 'vscode.executeCompletionItemProvider' ,
106+ uri ,
107+ position
108+ ) ;
109+ return ( completionList ?. items || [ ] ) . map ( item => item . label . toString ( ) ) ;
110+ }
111+
112+ /**
113+ * Definitions can return either direct locations or location links, so the
114+ * helper leaves that distinction to the individual assertion.
115+ */
116+ export async function getDefinitions ( uri : vscode . Uri , position : vscode . Position ) {
117+ return await vscode . commands . executeCommand < ( vscode . Location | vscode . LocationLink ) [ ] > (
118+ 'vscode.executeDefinitionProvider' ,
119+ uri ,
120+ position
121+ ) || [ ] ;
122+ }
123+
124+ /**
125+ * Searching by text keeps the tests readable because the fixture itself
126+ * shows the navigation target without hard-coded line numbers.
127+ */
128+ export function positionOf ( search : string ) {
129+ const index = doc . getText ( ) . indexOf ( search ) ;
130+ assert . ok ( index >= 0 , `Expected fixture to contain "${ search } "` ) ;
131+ return doc . positionAt ( index ) ;
132+ }
0 commit comments