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

Refactor storage.ts into testable modules and add unit tests#589

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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
EhabY merged 12 commits intocoder:mainfromEhabY:untangle-storage-ts
Sep 25, 2025
Merged
Show file tree
Hide file tree
Changes fromall commits
Commits
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
5 changes: 3 additions & 2 deletionspackage.json
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -316,7 +316,6 @@
"eventsource": "^3.0.6",
"find-process": "https://github.com/coder/find-process#fix/sequoia-compat",
"jsonc-parser": "^3.3.1",
"memfs": "^4.17.1",
"node-forge": "^1.3.1",
"openpgp": "^6.2.0",
"pretty-bytes": "^7.0.0",
Expand All@@ -336,6 +335,7 @@
"@types/ws": "^8.18.1",
"@typescript-eslint/eslint-plugin": "^7.0.0",
"@typescript-eslint/parser": "^6.21.0",
"@vitest/coverage-v8": "^3.2.4",
"@vscode/test-cli": "^0.0.11",
"@vscode/test-electron": "^2.5.2",
"@vscode/vsce": "^3.6.0",
Expand All@@ -350,12 +350,13 @@
"eslint-plugin-prettier": "^5.4.1",
"glob": "^10.4.2",
"jsonc-eslint-parser": "^2.4.0",
"memfs": "^4.46.0",
"nyc": "^17.1.0",
"prettier": "^3.5.3",
"ts-loader": "^9.5.1",
"typescript": "^5.8.3",
"utf-8-validate": "^6.0.5",
"vitest": "^0.34.6",
"vitest": "^3.2.4",
"vscode-test": "^1.5.0",
"webpack": "^5.99.6",
"webpack-cli": "^5.1.4"
Expand Down
274 changes: 274 additions & 0 deletionssrc/__mocks__/testHelpers.ts
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,274 @@
import { vi } from "vitest";
import * as vscode from "vscode";

/**
* Mock configuration provider that integrates with the vscode workspace configuration mock.
* Use this to set configuration values that will be returned by vscode.workspace.getConfiguration().
*/
export class MockConfigurationProvider {
private config = new Map<string, unknown>();

constructor() {
this.setupVSCodeMock();
}

/**
* Set a configuration value that will be returned by vscode.workspace.getConfiguration().get()
*/
set(key: string, value: unknown): void {
this.config.set(key, value);
}

/**
* Get a configuration value (for testing purposes)
*/
get<T>(key: string): T | undefined;
get<T>(key: string, defaultValue: T): T;
get<T>(key: string, defaultValue?: T): T | undefined {
const value = this.config.get(key);
return value !== undefined ? (value as T) : defaultValue;
}

/**
* Clear all configuration values
*/
clear(): void {
this.config.clear();
}

/**
* Setup the vscode.workspace.getConfiguration mock to return our values
*/
private setupVSCodeMock(): void {
vi.mocked(vscode.workspace.getConfiguration).mockImplementation(
(section?: string) => {
// Create a snapshot of the current config when getConfiguration is called
const snapshot = new Map(this.config);
const getFullKey = (part: string) =>
section ? `${section}.${part}` : part;

return {
get: vi.fn((key: string, defaultValue?: unknown) => {
const value = snapshot.get(getFullKey(key));
return value !== undefined ? value : defaultValue;
}),
has: vi.fn((key: string) => {
return snapshot.has(getFullKey(key));
}),
inspect: vi.fn(),
update: vi.fn((key: string, value: unknown) => {
this.config.set(getFullKey(key), value);
return Promise.resolve();
}),
};
},
);
}
}

/**
* Mock progress reporter that integrates with vscode.window.withProgress.
* Use this to control progress reporting behavior and cancellation in tests.
*/
export class MockProgressReporter {
private shouldCancel = false;
private progressReports: Array<{ message?: string; increment?: number }> = [];

constructor() {
this.setupVSCodeMock();
}

/**
* Set whether the progress should be cancelled
*/
setCancellation(cancel: boolean): void {
this.shouldCancel = cancel;
}

/**
* Get all progress reports that were made
*/
getProgressReports(): Array<{ message?: string; increment?: number }> {
return [...this.progressReports];
}

/**
* Clear all progress reports
*/
clearProgressReports(): void {
this.progressReports = [];
}

/**
* Setup the vscode.window.withProgress mock
*/
private setupVSCodeMock(): void {
vi.mocked(vscode.window.withProgress).mockImplementation(
async <T>(
_options: vscode.ProgressOptions,
task: (
progress: vscode.Progress<{ message?: string; increment?: number }>,
token: vscode.CancellationToken,
) => Thenable<T>,
): Promise<T> => {
const progress = {
report: vi.fn((value: { message?: string; increment?: number }) => {
this.progressReports.push(value);
}),
};

const cancellationToken: vscode.CancellationToken = {
isCancellationRequested: this.shouldCancel,
onCancellationRequested: vi.fn((listener: (x: unknown) => void) => {
if (this.shouldCancel) {
setTimeout(listener, 0);
}
return { dispose: vi.fn() };
}),
};

return task(progress, cancellationToken);
},
);
}
}

/**
* Mock user interaction that integrates with vscode.window message dialogs.
* Use this to control user responses in tests.
*/
export class MockUserInteraction {
private responses = new Map<string, string | undefined>();
private externalUrls: string[] = [];

constructor() {
this.setupVSCodeMock();
}

/**
* Set a response for a specific message
*/
setResponse(message: string, response: string | undefined): void {
this.responses.set(message, response);
}

/**
* Get all URLs that were opened externally
*/
getExternalUrls(): string[] {
return [...this.externalUrls];
}

/**
* Clear all external URLs
*/
clearExternalUrls(): void {
this.externalUrls = [];
}

/**
* Clear all responses
*/
clearResponses(): void {
this.responses.clear();
}

/**
* Setup the vscode.window message dialog mocks
*/
private setupVSCodeMock(): void {
const getResponse = (message: string): string | undefined => {
return this.responses.get(message);
};

vi.mocked(vscode.window.showErrorMessage).mockImplementation(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(message: string): Thenable<any> => {
const response = getResponse(message);
return Promise.resolve(response);
},
);

vi.mocked(vscode.window.showWarningMessage).mockImplementation(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(message: string): Thenable<any> => {
const response = getResponse(message);
return Promise.resolve(response);
},
);

vi.mocked(vscode.window.showInformationMessage).mockImplementation(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(message: string): Thenable<any> => {
const response = getResponse(message);
return Promise.resolve(response);
},
);

vi.mocked(vscode.env.openExternal).mockImplementation(
(target: vscode.Uri): Promise<boolean> => {
this.externalUrls.push(target.toString());
return Promise.resolve(true);
},
);
}
}

// Simple in-memory implementation of Memento
export class InMemoryMemento implements vscode.Memento {
private storage = new Map<string, unknown>();

get<T>(key: string): T | undefined;
get<T>(key: string, defaultValue: T): T;
get<T>(key: string, defaultValue?: T): T | undefined {
return this.storage.has(key) ? (this.storage.get(key) as T) : defaultValue;
}

async update(key: string, value: unknown): Promise<void> {
if (value === undefined) {
this.storage.delete(key);
} else {
this.storage.set(key, value);
}
return Promise.resolve();
}

keys(): readonly string[] {
return Array.from(this.storage.keys());
}
}

// Simple in-memory implementation of SecretStorage
export class InMemorySecretStorage implements vscode.SecretStorage {
private secrets = new Map<string, string>();
private isCorrupted = false;

onDidChange: vscode.Event<vscode.SecretStorageChangeEvent> = () => ({
dispose: () => {},
});

async get(key: string): Promise<string | undefined> {
if (this.isCorrupted) {
return Promise.reject(new Error("Storage corrupted"));
}
return this.secrets.get(key);
}

async store(key: string, value: string): Promise<void> {
if (this.isCorrupted) {
return Promise.reject(new Error("Storage corrupted"));
}
this.secrets.set(key, value);
}

async delete(key: string): Promise<void> {
if (this.isCorrupted) {
return Promise.reject(new Error("Storage corrupted"));
}
this.secrets.delete(key);
}

corruptStorage(): void {
this.isCorrupted = true;
}
}
Loading

[8]ページ先頭

©2009-2025 Movatter.jp