Skip to content

feat: migrate test suite from Mocha to Vitest #2815

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

Open
wants to merge 43 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
fe4c2ea
feat: migrate from Mocha to Vitest
aewing Aug 7, 2025
c637791
chore: remove unused cross-env dependency
aewing Aug 7, 2025
cdf8417
feat: improve Svelte 5 testing setup
aewing Aug 7, 2025
51f29e2
feat: pre-install both Svelte versions for parallel testing
aewing Aug 7, 2025
89def05
fix: improve multi-version testing with centralized version detection
aewing Aug 7, 2025
ddae917
fix: handle version detection correctly in multi-version testing
aewing Aug 7, 2025
b3bee6d
fix: address all deprecation warnings and improve configuration
aewing Aug 7, 2025
34f494b
fix: simplify dual version testing approach
aewing Aug 7, 2025
a4a0a5a
fix: add missing Position import in SveltePlugin test
aewing Aug 7, 2025
e86a41e
feat: complete migration from Mocha to Vitest
aewing Aug 7, 2025
fe3c44d
chore: remove duplicate snapshot file
aewing Aug 7, 2025
d7d8e1c
refactor: switch to file-based snapshots and restore original configu…
aewing Aug 7, 2025
84e03a9
improve: replace timeout-based performance test with assertion-based …
aewing Aug 7, 2025
0683aea
test(language-server): revert Vitest snapshots to file-based expected…
aewing Aug 14, 2025
e367b9f
test(language-server): add/update expectedv2.json snapshots for inlay…
aewing Aug 14, 2025
8486425
test(language-server): switch Diagnostics tests to expectedv2.json fi…
aewing Aug 14, 2025
3a25714
test(language-server): restore all expectedv2.json from git history w…
aewing Aug 14, 2025
373753a
test(language-server): accept both Svelte 4 (hyphen) and Svelte 5 (un…
aewing Aug 14, 2025
3f6673e
test(language-server): remove obsolete snapshot artifact directories …
aewing Aug 14, 2025
13a0af6
test(language-server): tolerate Svelte 4 vs 5 diagnostic differences …
aewing Aug 14, 2025
a3cdc13
test(language-server): update expectedv2.json snapshots across diagno…
aewing Aug 14, 2025
09f10ab
chore: align pnpm-lock.yaml with package.json resolutions (Svelte 4)
aewing Aug 14, 2025
e6afda5
merge: upstream/master into feat/migrate-mocha-to-vitest (resolve pnp…
aewing Aug 14, 2025
8cb353e
test(language-server): align inlayHints workspace to fixtures and res…
aewing Aug 14, 2025
5e37fce
fix: svelte5 inlayHints snapshot
aewing Aug 14, 2025
60ae10c
fix: correct test assertion syntax and TypeScript errors in vitest mi…
aewing Aug 14, 2025
c670f0d
fix: add .sequential to resource-intensive test suites to prevent int…
aewing Aug 14, 2025
8c13146
fix: resolve CI test failures for cross-platform compatibility
aewing Aug 14, 2025
adbce7b
style: format diagnostics test file with prettier
aewing Aug 14, 2025
0c64d36
feat: add Svelte version switching and testing scripts
aewing Aug 14, 2025
c8cb07c
fix: update Svelte version switching to use pnpm overrides
aewing Aug 14, 2025
eb65dac
fix: handle .v5 tests properly in inlayHints
aewing Aug 14, 2025
2544a72
chore: lint fixes
aewing Aug 14, 2025
fb559cb
fix: add cleanup to version switching script
aewing Aug 14, 2025
137075e
fix: remove version switching scripts
aewing Aug 14, 2025
9e3ab7e
fix: formatting and lockfile
aewing Aug 14, 2025
06a107d
fix: update test snapshots for TypeScript union type ordering changes
aewing Aug 14, 2025
d4aa05d
fix: restore deleted tsconfig.json and package.json test fixtures
aewing Aug 14, 2025
8e1cce2
fix: diagnostics feature workspace dir
aewing Aug 17, 2025
f6550c9
fix: regenerate svelte 5 expected json
aewing Aug 18, 2025
0037493
fix: restore unintentionally changed files
aewing Aug 18, 2025
9b0739f
fix: restore typescript diagnostics fixtures directory
aewing Aug 18, 2025
2fe9b4e
why is this snapshot different?
aewing Aug 18, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
"devDependencies": {
"cross-env": "^7.0.2",
"prettier": "~3.3.3",
"ts-node": "^10.0.0"
"ts-node": "^10.0.0",
"vitest": "^3.2.4"
},
"packageManager": "[email protected]"
}
7 changes: 2 additions & 5 deletions packages/language-server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"./bin/server.js": "./bin/server.js"
},
"scripts": {
"test": "cross-env TS_NODE_TRANSPILE_ONLY=true mocha --require ts-node/register \"test/**/*.test.ts\"",
"test": "vitest --run",
"build": "tsc",
"prepublishOnly": "npm run build",
"watch": "tsc -w"
Expand Down Expand Up @@ -42,13 +42,10 @@
"@types/estree": "^0.0.42",
"@types/globrex": "^0.1.4",
"@types/lodash": "^4.14.116",
"@types/mocha": "^9.1.0",
"@types/node": "^18.0.0",
"@types/sinon": "^7.5.2",
"cross-env": "^7.0.2",
"mocha": "^9.2.0",
"sinon": "^11.0.0",
"ts-node": "^10.0.0"
"vitest": "^3.2.4"
},
"dependencies": {
"@jridgewell/trace-mapping": "^0.3.25",
Expand Down
66 changes: 33 additions & 33 deletions packages/language-server/test/lib/documents/Document.test.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,32 @@
import * as assert from 'assert';
import { describe, it, expect } from 'vitest';
import { Document } from '../../../src/lib/documents';
import { Position } from 'vscode-languageserver';

describe('Document', () => {
it('gets the correct text', () => {
const document = new Document('file:///hello.svelte', '<h1>Hello, world!</h1>');
assert.strictEqual(document.getText(), '<h1>Hello, world!</h1>');
expect(document.getText()).toEqual('<h1>Hello, world!</h1>');
});

it('sets the text', () => {
const document = new Document('file:///hello.svelte', '<h1>Hello, world!</h1>');
document.setText('<h1>Hello, svelte!</h1>');
assert.strictEqual(document.getText(), '<h1>Hello, svelte!</h1>');
expect(document.getText()).toEqual('<h1>Hello, svelte!</h1>');
});

it('increments the version on edits', () => {
const document = new Document('file:///hello.svelte', 'hello');
assert.strictEqual(document.version, 0);
expect(document.version).toEqual(0);

document.setText('Hello, world!');
assert.strictEqual(document.version, 1);
expect(document.version).toEqual(1);
document.update('svelte', 7, 12);
assert.strictEqual(document.version, 2);
expect(document.version).toEqual(2);
});

it('recalculates the tag infos on edits', () => {
const document = new Document('file:///hello.svelte', '<script>a</script><style>b</style>');
assert.deepEqual(document.scriptInfo, {
expect(document.scriptInfo).toEqual({
content: 'a',
attributes: {},
start: 8,
Expand All @@ -35,7 +35,7 @@ describe('Document', () => {
endPos: Position.create(0, 9),
container: { start: 0, end: 18 }
});
assert.deepEqual(document.styleInfo, {
expect(document.styleInfo).toEqual({
content: 'b',
attributes: {},
start: 25,
Expand All @@ -46,7 +46,7 @@ describe('Document', () => {
});

document.setText('<script>b</script>');
assert.deepEqual(document.scriptInfo, {
expect(document.scriptInfo).toEqual({
content: 'b',
attributes: {},
start: 8,
Expand All @@ -55,76 +55,76 @@ describe('Document', () => {
endPos: Position.create(0, 9),
container: { start: 0, end: 18 }
});
assert.strictEqual(document.styleInfo, null);
expect(document.styleInfo).toEqual(null);
});

it('returns the correct file path', () => {
const document = new Document('file:///hello.svelte', 'hello');

assert.strictEqual(document.getFilePath(), '/hello.svelte');
expect(document.getFilePath()).toEqual('/hello.svelte');
});

it('returns null for non file urls', () => {
const document = new Document('ftp:///hello.svelte', 'hello');

assert.strictEqual(document.getFilePath(), null);
expect(document.getFilePath()).toEqual(null);
});

it('gets the text length', () => {
const document = new Document('file:///hello.svelte', 'Hello, world!');
assert.strictEqual(document.getTextLength(), 13);
expect(document.getTextLength()).toEqual(13);
});

it('updates the text range', () => {
const document = new Document('file:///hello.svelte', 'Hello, world!');
document.update('svelte', 7, 12);
assert.strictEqual(document.getText(), 'Hello, svelte!');
expect(document.getText()).toEqual('Hello, svelte!');
});

it('gets the correct position from offset', () => {
const document = new Document('file:///hello.svelte', 'Hello\nworld\n');
assert.deepStrictEqual(document.positionAt(1), { line: 0, character: 1 });
assert.deepStrictEqual(document.positionAt(9), { line: 1, character: 3 });
assert.deepStrictEqual(document.positionAt(12), { line: 2, character: 0 });
expect(document.positionAt(1)).toEqual({ line: 0, character: 1 });
expect(document.positionAt(9)).toEqual({ line: 1, character: 3 });
expect(document.positionAt(12)).toEqual({ line: 2, character: 0 });
});

it('gets the correct offset from position', () => {
const document = new Document('file:///hello.svelte', 'Hello\nworld\n');
assert.strictEqual(document.offsetAt({ line: 0, character: 1 }), 1);
assert.strictEqual(document.offsetAt({ line: 1, character: 3 }), 9);
assert.strictEqual(document.offsetAt({ line: 2, character: 0 }), 12);
expect(document.offsetAt({ line: 0, character: 1 })).toBe(1);
expect(document.offsetAt({ line: 1, character: 3 })).toBe(9);
expect(document.offsetAt({ line: 2, character: 0 })).toBe(12);
});

it('gets the correct position from offset with CRLF', () => {
const document = new Document('file:///hello.svelte', 'Hello\r\nworld\r\n');
assert.deepStrictEqual(document.positionAt(1), { line: 0, character: 1 });
assert.deepStrictEqual(document.positionAt(10), { line: 1, character: 3 });
assert.deepStrictEqual(document.positionAt(14), { line: 2, character: 0 });
expect(document.positionAt(1)).toEqual({ line: 0, character: 1 });
expect(document.positionAt(10)).toEqual({ line: 1, character: 3 });
expect(document.positionAt(14)).toEqual({ line: 2, character: 0 });
});

it('gets the correct offset from position with CRLF', () => {
const document = new Document('file:///hello.svelte', 'Hello\r\nworld\r\n');
assert.strictEqual(document.offsetAt({ line: 0, character: 1 }), 1);
assert.strictEqual(document.offsetAt({ line: 1, character: 3 }), 10);
assert.strictEqual(document.offsetAt({ line: 2, character: 0 }), 14);
expect(document.offsetAt({ line: 0, character: 1 })).toEqual(1);
expect(document.offsetAt({ line: 1, character: 3 })).toEqual(10);
expect(document.offsetAt({ line: 2, character: 0 })).toEqual(14);
});

it('limits the position when offset is out of bounds', () => {
const document = new Document('file:///hello.svelte', 'Hello\nworld\n');
assert.deepStrictEqual(document.positionAt(20), { line: 2, character: 0 });
assert.deepStrictEqual(document.positionAt(-1), { line: 0, character: 0 });
expect(document.positionAt(20)).toEqual({ line: 2, character: 0 });
expect(document.positionAt(-1)).toEqual({ line: 0, character: 0 });
});

it('limits the offset when position is out of bounds', () => {
const document = new Document('file:///hello.svelte', 'Hello\nworld\n');
assert.strictEqual(document.offsetAt({ line: 5, character: 0 }), 12);
assert.strictEqual(document.offsetAt({ line: 1, character: 20 }), 12);
assert.strictEqual(document.offsetAt({ line: -1, character: 0 }), 0);
expect(document.offsetAt({ line: 5, character: 0 })).toEqual(12);
expect(document.offsetAt({ line: 1, character: 20 })).toEqual(12);
expect(document.offsetAt({ line: -1, character: 0 })).toEqual(0);
});

it('supports empty contents', () => {
const document = new Document('file:///hello.svelte', '');
assert.strictEqual(document.offsetAt({ line: 0, character: 0 }), 0);
assert.deepStrictEqual(document.positionAt(0), { line: 0, character: 0 });
expect(document.offsetAt({ line: 0, character: 0 })).toEqual(0);
expect(document.positionAt(0)).toEqual({ line: 0, character: 0 });
});
});
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import sinon from 'sinon';
import * as assert from 'assert';
import { describe, it, expect } from 'vitest';
import { TextDocumentItem, Range } from 'vscode-languageserver-types';
import { DocumentManager, Document } from '../../../src/lib/documents';

Expand Down Expand Up @@ -65,7 +65,7 @@ describe('Document Manager', () => {
it("fails to update if document isn't open", () => {
const manager = new DocumentManager(createTextDocument);

assert.throws(() => manager.updateDocument(textDocument, []));
expect(() => manager.updateDocument(textDocument, [])).toThrow();
});

it('emits a document change event on open and update', () => {
Expand Down Expand Up @@ -109,6 +109,6 @@ describe('Document Manager', () => {
]
);

assert.ok(manager.get(textDocument.uri)!.version > firstVersion);
expect(manager.get(textDocument.uri)!.version > firstVersion).toBeTruthy();
});
});
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import * as assert from 'assert';
import { describe, it, expect } from 'vitest';
import { FragmentMapper, positionAt } from '../../../src/lib/documents';

describe('DocumentMapper', () => {
Expand All @@ -19,16 +19,16 @@ describe('DocumentMapper', () => {
it('isInGenerated works', () => {
const fragment = setup('Hello, \nworld!', 8, 13);

assert.strictEqual(fragment.isInGenerated({ line: 0, character: 0 }), false);
assert.strictEqual(fragment.isInGenerated({ line: 1, character: 0 }), true);
assert.strictEqual(fragment.isInGenerated({ line: 1, character: 5 }), true);
assert.strictEqual(fragment.isInGenerated({ line: 1, character: 6 }), false);
expect(fragment.isInGenerated({ line: 0, character: 0 })).toEqual(false);
expect(fragment.isInGenerated({ line: 1, character: 0 })).toEqual(true);
expect(fragment.isInGenerated({ line: 1, character: 5 })).toEqual(true);
expect(fragment.isInGenerated({ line: 1, character: 6 })).toEqual(false);
});

it('calculates the position in parent', () => {
const fragment = setup('Hello, \nworld!', 8, 13);

assert.deepStrictEqual(fragment.getOriginalPosition({ line: 0, character: 2 }), {
expect(fragment.getOriginalPosition({ line: 0, character: 2 })).toEqual({
line: 1,
character: 2
});
Expand All @@ -37,7 +37,7 @@ describe('DocumentMapper', () => {
it('calculates the position in fragment', () => {
const fragment = setup('Hello, \nworld!', 8, 13);

assert.deepStrictEqual(fragment.getGeneratedPosition({ line: 1, character: 2 }), {
expect(fragment.getGeneratedPosition({ line: 1, character: 2 })).toEqual({
line: 0,
character: 2
});
Expand Down
25 changes: 10 additions & 15 deletions packages/language-server/test/lib/documents/configLoader.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ConfigLoader } from '../../../src/lib/documents/configLoader';
import path from 'path';
import { pathToFileURL, URL } from 'url';
import assert from 'assert';
import { describe, it, expect } from 'vitest';
import { spy } from 'sinon';

describe('ConfigLoader', () => {
Expand Down Expand Up @@ -49,8 +49,8 @@ describe('ConfigLoader', () => {
) {
filePath = normalizePath(filePath);
configPath = normalizePath(configPath);
assert.deepStrictEqual(configLoader.getConfig(filePath), configFrom(configPath));
assert.deepStrictEqual(await configLoader.awaitConfig(filePath), configFrom(configPath));
expect(configLoader.getConfig(filePath)).toEqual(configFrom(configPath));
expect(await configLoader.awaitConfig(filePath)).toEqual(configFrom(configPath));
}

it('should load all config files below and the one inside/above given directory', async () => {
Expand Down Expand Up @@ -108,13 +108,12 @@ describe('ConfigLoader', () => {
);
await configLoader.loadConfigs(normalizePath('/some/path'));

assert.deepStrictEqual(
expect(
// Can't do the equal-check directly, instead check if it's the expected object props
Object.keys(
configLoader.getConfig(normalizePath('/some/path/comp.svelte'))?.preprocess || {}
).sort(),
['name', 'script'].sort()
);
).sort()
).toEqual(['name', 'script'].sort());
});

it('will not load config multiple times if config loading started in parallel', async () => {
Expand Down Expand Up @@ -158,17 +157,14 @@ describe('ConfigLoader', () => {
'/some/path/sub/comp.svelte',
'/some/path/svelte.config.js'
);
assert.deepStrictEqual(nrImportCalls, 1);
expect(nrImportCalls).toEqual(1);
});

it('can deal with missing config', () => {
const configLoader = new ConfigLoader(mockFdir([]), { existsSync: () => false }, path, () =>
Promise.resolve('unimportant')
);
assert.deepStrictEqual(
configLoader.getConfig(normalizePath('/some/file.svelte')),
undefined
);
expect(configLoader.getConfig(normalizePath('/some/file.svelte'))).toEqual(undefined);
});

it('should await config', async () => {
Expand All @@ -178,8 +174,7 @@ describe('ConfigLoader', () => {
path,
(module: URL) => Promise.resolve({ default: { preprocess: module.toString() } })
);
assert.deepStrictEqual(
await configLoader.awaitConfig(normalizePath('some/file.svelte')),
expect(await configLoader.awaitConfig(normalizePath('some/file.svelte'))).toEqual(
configFrom(normalizePath('some/svelte.config.js'))
);
});
Expand All @@ -194,6 +189,6 @@ describe('ConfigLoader', () => {
);
configLoader.setDisabled(true);
await configLoader.awaitConfig(normalizePath('some/file.svelte'));
assert.deepStrictEqual(moduleLoader.notCalled, true);
expect(moduleLoader.notCalled).toEqual(true);
});
});
Loading