Skip to content

Commit 7ef2839

Browse files
authored
Add outline integration testing! (#40)
1 parent 94acf25 commit 7ef2839

File tree

17 files changed

+899
-71
lines changed

17 files changed

+899
-71
lines changed

.eslintignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ src/configurationType.ts
44
src/configurationTypeCache.jsonc
55
playground.ts
66
test.ts
7+
integration/fixtures

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ generators
66
build
77
dist
88
out
9+
testsOut
910
.next
1011
.nuxt
1112
.cache

README.ext.md

Lines changed: 0 additions & 5 deletions
This file was deleted.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"include": [
3+
"src"
4+
]
5+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
const Component = () => {
2+
return (
3+
<div className="main__wrap">
4+
<main className="container">
5+
<div className="card__box">
6+
<NavBar totalCounters={this.state.counters.filter(c => c.value > 0).length} />
7+
<Counters
8+
counters={this.state.counters}
9+
onReset={this.handleReset}
10+
onIncrement={this.handleIncrement}
11+
onDecrement={this.handleDecrement}
12+
onDelete={this.handleDelete}
13+
onRestart={this.handleRestart}
14+
/>
15+
</div>
16+
</main>
17+
</div>
18+
)
19+
}

integration/index.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { join } from 'path'
2+
import glob from 'glob'
3+
import Mocha from 'mocha'
4+
5+
export const run = async () => {
6+
const mocha = new Mocha({
7+
color: true,
8+
parallel: false,
9+
timeout: process.env.CI ? 4500 : 2000,
10+
})
11+
const testsRoot = join(__dirname, './suite')
12+
await new Promise<void>(resolve => {
13+
glob('**/**.test.js', { cwd: testsRoot }, (err, files) => {
14+
if (err) throw err
15+
16+
for (const file of files) mocha.addFile(join(testsRoot, file))
17+
18+
mocha.run(failures => {
19+
if (failures > 0) {
20+
console.error(`${failures} tests failed.`)
21+
setImmediate(() => {
22+
process.exit(1)
23+
})
24+
} else {
25+
resolve()
26+
}
27+
})
28+
})
29+
})
30+
}

integration/prerun.mjs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
//@ts-check
2+
import fs from 'fs'
3+
4+
try {
5+
fs.unlinkSync('.vscode-test/user-data/User/settings.json')
6+
} catch {}
7+
8+
try {
9+
fs.unlinkSync('out/plugin-config.json')
10+
} catch {}

integration/runTests.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { join } from 'path'
2+
import { runTests } from '@vscode/test-electron'
3+
4+
async function main() {
5+
try {
6+
await runTests({
7+
version: 'stable',
8+
extensionDevelopmentPath: join(__dirname, '../out'),
9+
extensionTestsPath: join(__dirname, './index'),
10+
launchArgs: ['--disable-extensions'],
11+
})
12+
} catch (error) {
13+
console.error(error)
14+
console.error('Failed to run tests')
15+
process.exit(1)
16+
}
17+
}
18+
19+
void main()

integration/suite/outline.test.ts

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
import * as vscode from 'vscode'
2+
import delay from 'delay'
3+
import dedent from 'string-dedent'
4+
5+
import { expect } from 'chai'
6+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment, @typescript-eslint/prefer-ts-expect-error
7+
//@ts-ignore
8+
import type { Configuration } from '../../src/configurationType'
9+
import { fromFixtures, replaceEditorText } from './utils'
10+
11+
describe('Outline', () => {
12+
const content = dedent/* tsx */ `
13+
const classes = {
14+
header: '...',
15+
title: '...'
16+
}
17+
function A() {
18+
return <Notification className="test another" id="yes">
19+
before
20+
<div id="ok">
21+
<div />
22+
<span class="good" />
23+
</div>
24+
after
25+
</Notification>
26+
}
27+
`
28+
29+
let document: vscode.TextDocument
30+
const editor = () => vscode.window.activeTextEditor!
31+
32+
const getOutline = async () => vscode.commands.executeCommand('vscode.executeDocumentSymbolProvider', document.uri) as any
33+
34+
// let editor: vscode.TextEditor
35+
const startPos = new vscode.Position(0, 0)
36+
before(async () => {
37+
const configKey: keyof Configuration = 'patchOutline'
38+
const configValue: Configuration['patchOutline'] = true
39+
await vscode.workspace.getConfiguration('tsEssentialPlugins').update(configKey, configValue, vscode.ConfigurationTarget.Global)
40+
await delay(600)
41+
await vscode.workspace
42+
.openTextDocument({
43+
content,
44+
language: 'typescriptreact',
45+
})
46+
.then(async newDocument => {
47+
document = newDocument
48+
/* editor = */ await vscode.window.showTextDocument(document)
49+
})
50+
})
51+
52+
it('Outline untitled works', async () => {
53+
console.time('get outline')
54+
const data = await getOutline()
55+
console.timeEnd('get outline')
56+
expect(simplifyOutline(data)).to.deep.equal([
57+
{
58+
name: 'A',
59+
children: [
60+
{
61+
name: 'Notification.test.another#yes',
62+
children: [
63+
{
64+
name: 'div#ok',
65+
children: [
66+
{
67+
name: 'div',
68+
},
69+
{
70+
name: 'span.good',
71+
},
72+
],
73+
},
74+
],
75+
},
76+
],
77+
},
78+
])
79+
await vscode.commands.executeCommand('workbench.action.closeActiveEditor')
80+
})
81+
82+
describe('Outline in js project', () => {
83+
it('Initial', async () => {
84+
await vscode.commands.executeCommand('vscode.openFolder', vscode.Uri.file(fromFixtures('test-project-js')))
85+
await delay(500)
86+
await vscode.commands.executeCommand('vscode.open', vscode.Uri.file(fromFixtures('test-project-js/src/index.jsx')))
87+
await delay(600)
88+
document = editor().document
89+
const data = await getOutline()
90+
expect(simplifyOutline(data)).to.deep.equal(jsProjectExpectedOutline())
91+
await vscode.commands.executeCommand('workbench.action.closeActiveEditor')
92+
})
93+
94+
it('Reopen', async () => {
95+
await delay(500)
96+
await vscode.commands.executeCommand('vscode.open', vscode.Uri.file(fromFixtures('test-project-js/src/index.jsx')))
97+
await delay(600)
98+
document = editor().document
99+
const data = await getOutline()
100+
expect(simplifyOutline(data)).to.deep.equal(jsProjectExpectedOutline())
101+
})
102+
103+
it('Text change', async () => {
104+
const searchText = 'NavBar'
105+
const componentEndPos = document.positionAt(document.getText().indexOf(searchText) + searchText.length)
106+
await replaceEditorText(editor(), new vscode.Range(componentEndPos.translate(0, -1), componentEndPos), '2')
107+
await delay(500)
108+
const data = await getOutline()
109+
expect(simplifyOutline(data)).to.deep.equal(jsProjectExpectedOutline('NavBa2'))
110+
})
111+
112+
it('Text change right after TSServer restart', async () => {
113+
void vscode.commands.executeCommand('typescript.restartTsServer')
114+
const searchText = 'NavBa2'
115+
const componentEndPos = document.positionAt(document.getText().indexOf(searchText) + searchText.length)
116+
await editor().edit(builder => {
117+
builder.replace(new vscode.Range(componentEndPos.translate(0, -1), componentEndPos), '3')
118+
})
119+
await delay(800)
120+
const data = await getOutline()
121+
expect(simplifyOutline(data)).to.deep.equal(jsProjectExpectedOutline('NavBa3'))
122+
})
123+
124+
it('Text change with no syntax server', async () => {
125+
await vscode.workspace.getConfiguration('typescript').update('tsserver.useSyntaxServer', 'never', vscode.ConfigurationTarget.Global)
126+
// void vscode.commands.executeCommand('typescript.restartTsServer')
127+
await delay(300)
128+
const searchText = 'NavBa3'
129+
const componentEndPos = document.positionAt(document.getText().indexOf(searchText) + searchText.length)
130+
await editor().edit(builder => {
131+
builder.replace(new vscode.Range(componentEndPos.translate(0, -1), componentEndPos), '4')
132+
})
133+
await delay(800)
134+
const data = await getOutline()
135+
expect(simplifyOutline(data)).to.deep.equal(jsProjectExpectedOutline('NavBa4'))
136+
}).timeout(8000)
137+
})
138+
})
139+
140+
const jsProjectExpectedOutline = (navbarPosName = 'NavBar') => [
141+
{
142+
name: 'Component',
143+
children: [
144+
{
145+
name: 'div.main__wrap',
146+
children: [
147+
{
148+
name: 'main.container',
149+
children: [
150+
{
151+
name: 'div.card__box',
152+
children: [
153+
{
154+
name: navbarPosName,
155+
},
156+
{
157+
name: 'Counters',
158+
},
159+
],
160+
},
161+
],
162+
},
163+
],
164+
},
165+
],
166+
},
167+
]
168+
169+
const simplifyOutline = (items: Array<vscode.SymbolInformation & vscode.DocumentSymbol>) => {
170+
const newItems: Array<{ name: any; children? }> = []
171+
for (const { children, name } of items) {
172+
if (name === 'classes') continue
173+
newItems.push({ name, ...(children?.length ? { children: simplifyOutline(children as any) } : {}) })
174+
}
175+
176+
return newItems
177+
}

integration/suite/utils.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { join } from 'path'
2+
import * as vscode from 'vscode'
3+
4+
export const fromFixtures = (path: string) => join(__dirname, '../../integration/fixtures', path)
5+
6+
export const clearEditorText = async (editor: vscode.TextEditor, resetContent = '') => {
7+
await new Promise<void>(resolve => {
8+
const { document } = editor
9+
if (document.getText() === resetContent) {
10+
resolve()
11+
return
12+
}
13+
14+
const { dispose } = vscode.workspace.onDidChangeTextDocument(({ document }) => {
15+
if (document.uri !== editor.document.uri) return
16+
dispose()
17+
resolve()
18+
})
19+
void editor.edit(builder =>
20+
builder.replace(new vscode.Range(new vscode.Position(0, 0), document.lineAt(document.lineCount - 1).range.end), resetContent),
21+
)
22+
})
23+
}
24+
25+
export const replaceEditorText = async (editor: vscode.TextEditor, range: vscode.Range, text: string) => {
26+
await new Promise<void>(resolve => {
27+
const { document } = editor
28+
if (document.getText(range) === text) {
29+
resolve()
30+
return
31+
}
32+
33+
// eslint-disable-next-line sonarjs/no-identical-functions
34+
const { dispose } = vscode.workspace.onDidChangeTextDocument(({ document }) => {
35+
if (document.uri !== editor.document.uri) return
36+
dispose()
37+
resolve()
38+
})
39+
void editor.edit(builder => builder.replace(range, text))
40+
})
41+
}

0 commit comments

Comments
 (0)