Skip to content

Commit 73a4f5a

Browse files
authored
Merge branch 'main' into モック用エンジンを使ったe2eテストに差し替え
2 parents 39e7681 + b80b70e commit 73a4f5a

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

75 files changed

+1594
-763
lines changed

.env.test-electron-default-vvpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# VVPPデフォルトエンジンでのテスト用の.envファイル。
2+
3+
VITE_APP_NAME=voicevox
4+
VITE_DEFAULT_ENGINE_INFOS=`[
5+
{
6+
"type": "downloadVvpp",
7+
"name": "VOICEVOX Nemo Engine",
8+
"uuid": "208cf94d-43d2-4cf5-abc0-9783cac36d29",
9+
"executionEnabled": true,
10+
"executionArgs": [],
11+
"host": "http://127.0.0.1:50121",
12+
"latestUrl": "https://voicevox.hiroshiba.jp/nemoLatestDefaultEngineInfos.json"
13+
}
14+
]`
15+
VITE_OFFICIAL_WEBSITE_URL=https://voicevox.hiroshiba.jp/
16+
VITE_LATEST_UPDATE_INFOS_URL=https://voicevox.hiroshiba.jp/updateInfos.json
17+
VITE_GTM_CONTAINER_ID=GTM-DUMMY

.github/actions/setup-environment/action.yml

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,5 @@ runs:
3333
restore-keys: |
3434
${{ env.cache-version }}-${{ runner.os }}--electron-builder-cache-
3535
36-
- name: Cache external dependencies
37-
uses: actions/cache@v4
38-
with:
39-
path: ./build/vendored
40-
key: ${{ env.cache-version }}-${{ runner.os }}--vendored-${{ hashFiles('build/*.js') }}
41-
restore-keys: |
42-
${{ env.cache-version }}-${{ runner.os }}--vendored-
43-
4436
- shell: bash
4537
run: npm ci

.github/workflows/build.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ on:
2626
default: false
2727

2828
env:
29-
VOICEVOX_ENGINE_VERSION: 0.22.1
30-
VOICEVOX_RESOURCE_VERSION: 0.22.1
29+
VOICEVOX_ENGINE_VERSION: 0.22.2
30+
VOICEVOX_RESOURCE_VERSION: 0.22.2
3131
VOICEVOX_EDITOR_VERSION:
3232
|- # releaseタグ名か、workflow_dispatchでのバージョン名か、999.999.999-developが入る
3333
${{ github.event.release.tag_name || github.event.inputs.version || '999.999.999-develop' }}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
name: "Merge Gatekeeper"
2+
3+
# auto mergeとmerge queue用のチェッカー。
4+
# Approve数が足りているか、すべてのテストが通っているかを確認します。
5+
# 詳細: https://github.com/VOICEVOX/merge-gatekeeper
6+
7+
on:
8+
pull_request_target:
9+
types: [auto_merge_enabled]
10+
merge_group:
11+
types: [checks_requested]
12+
13+
jobs:
14+
merge_gatekeeper:
15+
runs-on: ubuntu-latest
16+
steps:
17+
- uses: voicevox/merge-gatekeeper@main
18+
with:
19+
token: ${{ secrets.GATEKEEPER_TOKEN }}
20+
required_score: 2
21+
score_rules: |
22+
#maintainer: 2
23+
#reviewer: 1
24+
- uses: upsidr/merge-gatekeeper@v1
25+
with:
26+
token: ${{ secrets.GITHUB_TOKEN }}
27+
self: merge_gatekeeper
28+
# https://github.com/upsidr/merge-gatekeeper/issues/71#issuecomment-1660607977
29+
ref: ${{ github.event.pull_request && github.event.pull_request.head.sha || github.ref }}
30+
timeout: 18000 # 5 hours

.github/workflows/test.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ on:
55
pull_request:
66
branches:
77
- "**"
8+
merge_group:
9+
types: [checks_requested]
810
workflow_dispatch:
911

1012
env:

openapi.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

public/updateInfos.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
11
[
2+
{
3+
"version": "0.22.3",
4+
"descriptions": ["キャラクター「ぞん子」を追加"],
5+
"contributors": []
6+
},
27
{
38
"version": "0.22.2",
49
"descriptions": ["キャラクター「中部つるぎ」を追加"],

src/backend/browser/browserConfig.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { BaseConfigManager, Metadata } from "@/backend/common/ConfigManager";
55
import { ConfigType, EngineId, engineSettingSchema } from "@/type/preload";
66
import { ensureNotNullish } from "@/helpers/errorHelper";
77
import { UnreachableError } from "@/type/utility";
8+
import { isMac } from "@/helpers/platform";
89

910
const dbName = `${import.meta.env.VITE_APP_NAME}-web`;
1011
const settingStoreKey = "config";
@@ -20,7 +21,7 @@ const defaultEngineId = EngineId(defaultEngine.uuid);
2021
export async function getConfigManager() {
2122
await configManagerLock.acquire("configManager", async () => {
2223
if (!configManager) {
23-
configManager = new BrowserConfigManager();
24+
configManager = new BrowserConfigManager({ isMac });
2425
await configManager.initialize();
2526
}
2627
});

src/backend/browser/fakePath.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { z } from "zod";
2+
import { uuid4 } from "@/helpers/random";
3+
4+
const fakePathSchema = z
5+
.string()
6+
.regex(/^<browser-dummy-[0-9a-f]+>-.+$/)
7+
.brand("FakePath");
8+
export type FakePath = z.infer<typeof fakePathSchema>;
9+
10+
export const isFakePath = (path: string): path is FakePath => {
11+
return fakePathSchema.safeParse(path).success;
12+
};
13+
14+
export const createFakePath = (name: string): FakePath => {
15+
return fakePathSchema.parse(`<browser-dummy-${uuid4()}>-${name}`);
16+
};

src/backend/browser/fileImpl.ts

Lines changed: 92 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import { directoryHandleStoreKey } from "./contract";
22
import { openDB } from "./browserConfig";
3+
import { createFakePath, FakePath, isFakePath } from "./fakePath";
34
import { SandboxKey } from "@/type/preload";
45
import { failure, success } from "@/type/result";
56
import { createLogger } from "@/domain/frontend/log";
6-
import { uuid4 } from "@/helpers/random";
77
import { normalizeError } from "@/helpers/normalizeError";
88
import path from "@/helpers/path";
9+
import { ExhaustiveError } from "@/type/utility";
910

1011
const log = createLogger("fileImpl");
1112

@@ -113,45 +114,81 @@ const getDirectoryHandleFromDirectoryPath = async (
113114
}
114115
};
115116

117+
export type WritableFilePath =
118+
| {
119+
// ファイル名のみ。ダウンロードとして扱われます。
120+
type: "nameOnly";
121+
path: string;
122+
}
123+
| {
124+
// ディレクトリ内への書き込み。
125+
type: "child";
126+
path: string;
127+
}
128+
| {
129+
// 疑似パス。
130+
type: "fake";
131+
path: FakePath;
132+
};
133+
116134
// NOTE: fixedExportEnabled が有効になっている GENERATE_AND_SAVE_AUDIO action では、ファイル名に加えディレクトリ名も指定された状態でfilePathが渡ってくる
117135
// また GENERATE_AND_SAVE_ALL_AUDIO action では fixedExportEnabled の有効の有無に関わらず、ディレクトリ名も指定された状態でfilePathが渡ってくる
118-
export const writeFileImpl: (typeof window)[typeof SandboxKey]["writeFile"] =
119-
async (obj: { filePath: string; buffer: ArrayBuffer }) => {
120-
const filePath = obj.filePath;
136+
// showExportFilePicker での疑似パスが渡ってくる可能性もある。
137+
export const writeFileImpl = async (obj: {
138+
filePath: WritableFilePath;
139+
buffer: ArrayBuffer;
140+
}) => {
141+
const filePath = obj.filePath;
121142

122-
if (!filePath.includes(path.SEPARATOR)) {
143+
switch (filePath.type) {
144+
case "fake": {
145+
const fileHandle = fileHandleMap.get(filePath.path);
146+
if (fileHandle == undefined) {
147+
return failure(new Error(`ファイルが見つかりません: ${filePath.path}`));
148+
}
149+
const writable = await fileHandle.createWritable();
150+
await writable.write(obj.buffer);
151+
return writable.close().then(() => success(undefined));
152+
}
153+
154+
case "nameOnly": {
123155
const aTag = document.createElement("a");
124156
const blob = URL.createObjectURL(new Blob([obj.buffer]));
125157
aTag.href = blob;
126-
aTag.download = filePath;
158+
aTag.download = filePath.path;
127159
document.body.appendChild(aTag);
128160
aTag.click();
129161
document.body.removeChild(aTag);
130162
URL.revokeObjectURL(blob);
131163
return success(undefined);
132164
}
133165

134-
const fileName = resolveFileName(filePath);
135-
const maybeDirectoryHandleName = resolveDirectoryName(filePath);
166+
case "child": {
167+
const fileName = resolveFileName(filePath.path);
168+
const maybeDirectoryHandleName = resolveDirectoryName(filePath.path);
136169

137-
const directoryHandle = await getDirectoryHandleFromDirectoryPath(
138-
maybeDirectoryHandleName,
139-
);
170+
const directoryHandle = await getDirectoryHandleFromDirectoryPath(
171+
maybeDirectoryHandleName,
172+
);
140173

141-
directoryHandleMap.set(maybeDirectoryHandleName, directoryHandle);
174+
directoryHandleMap.set(maybeDirectoryHandleName, directoryHandle);
142175

143-
return directoryHandle
144-
.getFileHandle(fileName, { create: true })
145-
.then(async (fileHandle) => {
146-
const writable = await fileHandle.createWritable();
147-
await writable.write(obj.buffer);
148-
return writable.close();
149-
})
150-
.then(() => success(undefined))
151-
.catch((e) => {
152-
return failure(normalizeError(e));
153-
});
154-
};
176+
return directoryHandle
177+
.getFileHandle(fileName, { create: true })
178+
.then(async (fileHandle) => {
179+
const writable = await fileHandle.createWritable();
180+
await writable.write(obj.buffer);
181+
return writable.close();
182+
})
183+
.then(() => success(undefined))
184+
.catch((e) => {
185+
return failure(normalizeError(e));
186+
});
187+
}
188+
default:
189+
throw new ExhaustiveError(filePath);
190+
}
191+
};
155192

156193
export const checkFileExistsImpl: (typeof window)[typeof SandboxKey]["checkFileExists"] =
157194
async (filePath) => {
@@ -182,7 +219,7 @@ export const checkFileExistsImpl: (typeof window)[typeof SandboxKey]["checkFileE
182219
};
183220

184221
// FileSystemFileHandleを保持するMap。キーは生成した疑似パス。
185-
const fileHandleMap: Map<string, FileSystemFileHandle> = new Map();
222+
const fileHandleMap: Map<FakePath, FileSystemFileHandle> = new Map();
186223

187224
// ファイル選択ダイアログを開く
188225
// 返り値はファイルパスではなく、疑似パスを返す
@@ -201,7 +238,7 @@ export const showOpenFilePickerImpl = async (options: {
201238
});
202239
const paths = [];
203240
for (const handle of handles) {
204-
const fakePath = `<browser-dummy-${uuid4()}>-${handle.name}`;
241+
const fakePath = createFakePath(handle.name);
205242
fileHandleMap.set(fakePath, handle);
206243
paths.push(fakePath);
207244
}
@@ -214,6 +251,9 @@ export const showOpenFilePickerImpl = async (options: {
214251

215252
// 指定した疑似パスのファイルを読み込む
216253
export const readFileImpl = async (filePath: string) => {
254+
if (!isFakePath(filePath)) {
255+
return failure(new Error(`疑似パスではありません: ${filePath}`));
256+
}
217257
const fileHandle = fileHandleMap.get(filePath);
218258
if (fileHandle == undefined) {
219259
return failure(new Error(`ファイルが見つかりません: ${filePath}`));
@@ -222,3 +262,29 @@ export const readFileImpl = async (filePath: string) => {
222262
const buffer = await file.arrayBuffer();
223263
return success(buffer);
224264
};
265+
266+
// ファイル選択ダイアログを開く
267+
// 返り値はファイルパスではなく、疑似パスを返す
268+
export const showExportFilePickerImpl: (typeof window)[typeof SandboxKey]["showExportFileDialog"] =
269+
async (obj: {
270+
defaultPath?: string;
271+
extensionName: string;
272+
extensions: string[];
273+
title: string;
274+
}) => {
275+
const handle = await showSaveFilePicker({
276+
suggestedName: obj.defaultPath,
277+
types: [
278+
{
279+
description: obj.extensions.join("、"),
280+
accept: {
281+
"application/octet-stream": obj.extensions.map((ext) => `.${ext}`),
282+
},
283+
},
284+
],
285+
});
286+
const fakePath = createFakePath(handle.name);
287+
fileHandleMap.set(fakePath, handle);
288+
289+
return fakePath;
290+
};

0 commit comments

Comments
 (0)