Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit0b42207

Browse files
committed
Add unit tests for the split modules
1 parent3c3cb5e commit0b42207

File tree

7 files changed

+412
-29
lines changed

7 files changed

+412
-29
lines changed

‎src/__mocks__/testHelpers.ts‎

Lines changed: 15 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,15 @@ import * as vscode from "vscode";
88
exportclassMockConfigurationProvider{
99
privateconfig=newMap<string,unknown>();
1010

11+
constructor(){
12+
this.setupVSCodeMock();
13+
}
14+
1115
/**
1216
* Set a configuration value that will be returned by vscode.workspace.getConfiguration().get()
1317
*/
1418
set(key:string,value:unknown):void{
1519
this.config.set(key,value);
16-
this.setupVSCodeMock();
1720
}
1821

1922
/**
@@ -31,13 +34,12 @@ export class MockConfigurationProvider {
3134
*/
3235
clear():void{
3336
this.config.clear();
34-
this.setupVSCodeMock();
3537
}
3638

3739
/**
3840
* Setup the vscode.workspace.getConfiguration mock to return our values
3941
*/
40-
setupVSCodeMock():void{
42+
privatesetupVSCodeMock():void{
4143
vi.mocked(vscode.workspace.getConfiguration).mockReturnValue({
4244
get:vi.fn((key:string,defaultValue?:unknown)=>{
4345
constvalue=this.config.get(key);
@@ -58,6 +60,10 @@ export class MockProgressReporter {
5860
privateshouldCancel=false;
5961
privateprogressReports:Array<{message?:string;increment?:number}>=[];
6062

63+
constructor(){
64+
this.setupVSCodeMock();
65+
}
66+
6167
/**
6268
* Set whether the progress should be cancelled
6369
*/
@@ -82,7 +88,7 @@ export class MockProgressReporter {
8288
/**
8389
* Setup the vscode.window.withProgress mock
8490
*/
85-
setupVSCodeMock():void{
91+
privatesetupVSCodeMock():void{
8692
vi.mocked(vscode.window.withProgress).mockImplementation(
8793
async<T>(
8894
_options:vscode.ProgressOptions,
@@ -121,6 +127,10 @@ export class MockUserInteraction {
121127
privateresponses=newMap<string,string|undefined>();
122128
privateexternalUrls:string[]=[];
123129

130+
constructor(){
131+
this.setupVSCodeMock();
132+
}
133+
124134
/**
125135
* Set a response for a specific message or set a default response
126136
*/
@@ -163,7 +173,7 @@ export class MockUserInteraction {
163173
/**
164174
* Setup the vscode.window message dialog mocks
165175
*/
166-
setupVSCodeMock():void{
176+
privatesetupVSCodeMock():void{
167177
constgetResponse=(message:string):string|undefined=>{
168178
returnthis.responses.get(message)??this.responses.get("default");
169179
};
@@ -200,24 +210,3 @@ export class MockUserInteraction {
200210
);
201211
}
202212
}
203-
204-
/**
205-
* Helper function to setup all VS Code mocks for testing.
206-
* Call this in your test setup to initialize all the mock integrations.
207-
*/
208-
exportfunctionsetupVSCodeMocks():{
209-
mockConfig:MockConfigurationProvider;
210-
mockProgress:MockProgressReporter;
211-
mockUI:MockUserInteraction;
212-
}{
213-
constmockConfig=newMockConfigurationProvider();
214-
constmockProgress=newMockProgressReporter();
215-
constmockUI=newMockUserInteraction();
216-
217-
// Setup all the VS Code API mocks
218-
mockConfig.setupVSCodeMock();
219-
mockProgress.setupVSCodeMock();
220-
mockUI.setupVSCodeMock();
221-
222-
return{ mockConfig, mockProgress, mockUI};
223-
}

‎src/core/binaryManager.test.ts‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import {
99
MockConfigurationProvider,
1010
MockProgressReporter,
1111
MockUserInteraction,
12-
setupVSCodeMocks,
1312
}from"../__mocks__/testHelpers";
1413
import*asclifrom"../cliManager";
1514
import{Logger}from"../logging/logger";
@@ -45,7 +44,9 @@ describe("BinaryManager", () => {
4544
mockApi=createMockApi(TEST_VERSION,TEST_URL);
4645
mockAxios=mockApi.getAxiosInstance();
4746
vi.mocked(globalAxios.create).mockReturnValue(mockAxios);
48-
({ mockConfig, mockProgress, mockUI}=setupVSCodeMocks());
47+
mockConfig=newMockConfigurationProvider();
48+
mockProgress=newMockProgressReporter();
49+
mockUI=newMockUserInteraction();
4950
manager=newBinaryManager(
5051
mockLogger,
5152
newPathResolver("/path/base","/code/log"),

‎src/core/cliConfig.test.ts‎

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
importfsfrom"fs/promises";
2+
import{describe,it,expect,vi,beforeEach}from"vitest";
3+
import{CliConfigManager}from"./cliConfig";
4+
import{PathResolver}from"./pathResolver";
5+
6+
vi.mock("fs/promises");
7+
8+
describe("CliConfigManager",()=>{
9+
letpathResolver:PathResolver;
10+
letcliConfigManager:CliConfigManager;
11+
constmockFs=vi.mocked(fs);
12+
constwrittenFiles=newMap<string,string>();
13+
14+
beforeEach(()=>{
15+
vi.resetAllMocks();
16+
writtenFiles.clear();
17+
pathResolver=newPathResolver("/test/base","/test/log");
18+
cliConfigManager=newCliConfigManager(pathResolver);
19+
20+
mockFs.mkdir.mockResolvedValue(undefined);
21+
mockFs.writeFile.mockImplementation(async(path,content)=>{
22+
writtenFiles.set(path.toString(),content.toString());
23+
returnPromise.resolve();
24+
});
25+
});
26+
27+
describe("configure",()=>{
28+
it("should write both url and token to correct paths",async()=>{
29+
awaitcliConfigManager.configure(
30+
"deployment",
31+
"https://coder.example.com",
32+
"test-token",
33+
);
34+
35+
expect([...writtenFiles.entries()]).toEqual([
36+
["/test/base/deployment/url","https://coder.example.com"],
37+
["/test/base/deployment/session","test-token"],
38+
]);
39+
});
40+
41+
it("should skip URL when undefined but write token",async()=>{
42+
awaitcliConfigManager.configure("deployment",undefined,"test-token");
43+
44+
// No entry for the url
45+
expect([...writtenFiles.entries()]).toEqual([
46+
["/test/base/deployment/session","test-token"],
47+
]);
48+
});
49+
50+
it("should skip token when null but write URL",async()=>{
51+
awaitcliConfigManager.configure(
52+
"deployment",
53+
"https://coder.example.com",
54+
null,
55+
);
56+
57+
// No entry for the session
58+
expect([...writtenFiles.entries()]).toEqual([
59+
["/test/base/deployment/url","https://coder.example.com"],
60+
]);
61+
});
62+
63+
it("should write empty string for token when provided",async()=>{
64+
awaitcliConfigManager.configure(
65+
"deployment",
66+
"https://coder.example.com",
67+
"",
68+
);
69+
70+
expect([...writtenFiles.entries()]).toEqual([
71+
["/test/base/deployment/url","https://coder.example.com"],
72+
["/test/base/deployment/session",""],
73+
]);
74+
});
75+
76+
it("should use base path directly when label is empty",async()=>{
77+
awaitcliConfigManager.configure(
78+
"",
79+
"https://coder.example.com",
80+
"token",
81+
);
82+
83+
expect([...writtenFiles.entries()]).toEqual([
84+
["/test/base/url","https://coder.example.com"],
85+
["/test/base/session","token"],
86+
]);
87+
});
88+
});
89+
90+
describe("readConfig",()=>{
91+
beforeEach(()=>{
92+
mockFs.readFile.mockImplementation(async(filePath)=>{
93+
constpath=filePath.toString();
94+
if(writtenFiles.has(path)){
95+
returnwrittenFiles.get(path)!;
96+
}
97+
returnPromise.reject(newError("ENOENT: no such file or directory"));
98+
});
99+
});
100+
101+
it("should read and trim stored configuration",async()=>{
102+
writtenFiles.set(
103+
"/test/base/deployment/url",
104+
" https://coder.example.com \n",
105+
);
106+
writtenFiles.set("/test/base/deployment/session","\t test-token \r\n");
107+
108+
constresult=awaitcliConfigManager.readConfig("deployment");
109+
110+
expect(result).toEqual({
111+
url:"https://coder.example.com",
112+
token:"test-token",
113+
});
114+
});
115+
116+
it("should return empty strings for missing files",async()=>{
117+
constresult=awaitcliConfigManager.readConfig("deployment");
118+
119+
expect(result).toEqual({
120+
url:"",
121+
token:"",
122+
});
123+
});
124+
125+
it("should handle partial configuration",async()=>{
126+
writtenFiles.set(
127+
"/test/base/deployment/url",
128+
"https://coder.example.com",
129+
);
130+
131+
constresult=awaitcliConfigManager.readConfig("deployment");
132+
133+
expect(result).toEqual({
134+
url:"https://coder.example.com",
135+
token:"",
136+
});
137+
});
138+
});
139+
});

‎src/core/mementoManager.test.ts‎

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
import{describe,it,expect,beforeEach}from"vitest";
2+
importtype{Memento}from"vscode";
3+
import{MementoManager}from"./mementoManager";
4+
5+
// Simple in-memory implementation of Memento
6+
classInMemoryMementoimplementsMemento{
7+
privatestorage=newMap<string,unknown>();
8+
9+
get<T>(key:string):T|undefined;
10+
get<T>(key:string,defaultValue:T):T;
11+
get<T>(key:string,defaultValue?:T):T|undefined{
12+
returnthis.storage.has(key) ?(this.storage.get(key)asT) :defaultValue;
13+
}
14+
15+
asyncupdate(key:string,value:unknown):Promise<void>{
16+
if(value===undefined){
17+
this.storage.delete(key);
18+
}else{
19+
this.storage.set(key,value);
20+
}
21+
returnPromise.resolve();
22+
}
23+
24+
keys():readonlystring[]{
25+
returnArray.from(this.storage.keys());
26+
}
27+
}
28+
29+
describe("MementoManager",()=>{
30+
letmemento:InMemoryMemento;
31+
letmementoManager:MementoManager;
32+
33+
beforeEach(()=>{
34+
memento=newInMemoryMemento();
35+
mementoManager=newMementoManager(memento);
36+
});
37+
38+
describe("setUrl",()=>{
39+
it("should store URL and add to history",async()=>{
40+
awaitmementoManager.setUrl("https://coder.example.com");
41+
42+
expect(mementoManager.getUrl()).toBe("https://coder.example.com");
43+
expect(memento.get("urlHistory")).toEqual(["https://coder.example.com"]);
44+
});
45+
46+
it("should not update history for falsy values",async()=>{
47+
awaitmementoManager.setUrl(undefined);
48+
expect(mementoManager.getUrl()).toBeUndefined();
49+
expect(memento.get("urlHistory")).toBeUndefined();
50+
51+
awaitmementoManager.setUrl("");
52+
expect(mementoManager.getUrl()).toBe("");
53+
expect(memento.get("urlHistory")).toBeUndefined();
54+
});
55+
56+
it("should deduplicate URLs in history",async()=>{
57+
awaitmementoManager.setUrl("url1");
58+
awaitmementoManager.setUrl("url2");
59+
awaitmementoManager.setUrl("url1");// Re-add first URL
60+
61+
expect(memento.get("urlHistory")).toEqual(["url2","url1"]);
62+
});
63+
});
64+
65+
describe("withUrlHistory",()=>{
66+
it("should append URLs and remove duplicates",async()=>{
67+
awaitmemento.update("urlHistory",["existing1","existing2"]);
68+
69+
constresult=mementoManager.withUrlHistory("existing2","new1");
70+
71+
expect(result).toEqual(["existing1","existing2","new1"]);
72+
});
73+
74+
it("should limit to 10 URLs",async()=>{
75+
consturls=Array.from({length:10},(_,i)=>`url${i}`);
76+
awaitmemento.update("urlHistory",urls);
77+
78+
constresult=mementoManager.withUrlHistory("url20");
79+
80+
expect(result).toHaveLength(10);
81+
expect(result[0]).toBe("url1");
82+
expect(result[9]).toBe("url20");
83+
});
84+
85+
it("should handle non-array storage gracefully",async()=>{
86+
awaitmemento.update("urlHistory","not-an-array");
87+
constresult=mementoManager.withUrlHistory("url1");
88+
expect(result).toEqual(["url1"]);
89+
});
90+
});
91+
92+
describe("firstConnect",()=>{
93+
it("should return true only once",async()=>{
94+
awaitmementoManager.setFirstConnect();
95+
96+
expect(awaitmementoManager.getAndClearFirstConnect()).toBe(true);
97+
expect(awaitmementoManager.getAndClearFirstConnect()).toBe(false);
98+
});
99+
100+
it("should return false for non-boolean values",async()=>{
101+
awaitmemento.update("firstConnect","truthy-string");
102+
expect(awaitmementoManager.getAndClearFirstConnect()).toBe(false);
103+
});
104+
});
105+
});

‎src/core/mementoManager.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ export class MementoManager {
3030

3131
/**
3232
* Get the most recently accessed URLs (oldest to newest) with the provided
33-
* values appended.Duplicates will be removed.
33+
* values appended. Duplicates will be removed.
3434
*/
3535
publicwithUrlHistory(...append:(string|undefined)[]):string[]{
3636
constval=this.memento.get("urlHistory");

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp