Skip to content
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

Vuexのコマンドのsnapshotテストを追加 #2175

Merged
merged 12 commits into from
Jul 24, 2024
3 changes: 2 additions & 1 deletion src/backend/browser/fileImpl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { openDB } from "./browserConfig";
import { SandboxKey } from "@/type/preload";
import { failure, success } from "@/type/result";
import { createLogger } from "@/domain/frontend/log";
import { uuid4 } from "@/helpers/random";

const log = createLogger("fileImpl");

Expand Down Expand Up @@ -200,7 +201,7 @@ export const showOpenFilePickerImpl = async (options: {
});
const paths = [];
for (const handle of handles) {
const fakePath = `<browser-dummy-${crypto.randomUUID()}>-${handle.name}`;
const fakePath = `<browser-dummy-${uuid4()}>-${handle.name}`;
fileHandleMap.set(fakePath, handle);
paths.push(fakePath);
}
Expand Down
3 changes: 2 additions & 1 deletion src/components/Sing/ScoreSequencer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ import {
import { applyGaussianFilter, linearInterpolation } from "@/sing/utility";
import { useLyricInput } from "@/composables/useLyricInput";
import { ExhaustiveError } from "@/type/utility";
import { uuid4 } from "@/helpers/random";

type PreviewMode =
| "ADD_NOTE"
Expand Down Expand Up @@ -720,7 +721,7 @@ const startPreview = (event: MouseEvent, mode: PreviewMode, note?: Note) => {
return;
}
note = {
id: NoteId(crypto.randomUUID()),
id: NoteId(uuid4()),
position: guideLineTicks,
duration: snapTicks.value,
noteNumber: cursorNoteNumber,
Expand Down
27 changes: 27 additions & 0 deletions src/helpers/random.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/**
* 乱数値を生成する。モックに対応している。
* モックモードでは呼ばれた回数に応じて固定の値を返す。
*/

let mockMode = false;
let mockCount = 0;

/**
* モックモードにし、呼ばれた回数をリセットする。
*/
export function resetMockMode(): void {
mockMode = true;
mockCount = 0;
}

/**
* v4 UUID を生成する。
Hiroshiba marked this conversation as resolved.
Show resolved Hide resolved
*/
export function uuid4(): string {
if (!mockMode) {
return crypto.randomUUID();
} else {
mockCount++;
return `00000000-0000-0000-0000-${mockCount.toString().padStart(12, "0")}`;
Hiroshiba marked this conversation as resolved.
Show resolved Hide resolved
}
}
3 changes: 2 additions & 1 deletion src/sing/utaformatixProject/toVoicevox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { DEFAULT_TPQN, createDefaultTrack } from "@/sing/domain";
import { getDoremiFromNoteNumber } from "@/sing/viewHelper";
import { NoteId } from "@/type/preload";
import { Note, Tempo, TimeSignature, Track } from "@/store/type";
import { uuid4 } from "@/helpers/random";

/** UtaformatixのプロジェクトをVoicevoxの楽譜データに変換する */
export const ufProjectToVoicevox = (project: UfProject): VoicevoxScore => {
Expand Down Expand Up @@ -72,7 +73,7 @@ export const ufProjectToVoicevox = (project: UfProject): VoicevoxScore => {

const notes = trackNotes.map((value): Note => {
return {
id: NoteId(crypto.randomUUID()),
id: NoteId(uuid4()),
position: convertPosition(value.tickOn, projectTpqn, tpqn),
duration: convertDuration(
value.tickOn,
Expand Down
3 changes: 2 additions & 1 deletion src/store/audio.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,10 @@ import { AudioQuery, AccentPhrase, Speaker, SpeakerInfo } from "@/openapi";
import { base64ImageToUri, base64ToUri } from "@/helpers/base64Helper";
import { getValueOrThrow, ResultError } from "@/type/result";
import { generateWriteErrorMessage } from "@/helpers/fileHelper";
import { uuid4 } from "@/helpers/random";

function generateAudioKey() {
return AudioKey(crypto.randomUUID());
return AudioKey(uuid4());
}

function parseTextFile(
Expand Down
34 changes: 11 additions & 23 deletions src/store/command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ import {
MutationsBase,
MutationTree,
} from "@/store/vuex";
import { EditorType } from "@/type/preload";
import { CommandId, EditorType } from "@/type/preload";
import { uuid4 } from "@/helpers/random";

enablePatches();
enableMapSet();
Expand Down Expand Up @@ -67,7 +68,7 @@ const recordPatches =
(draft: S) => recipe(draft, payload),
);
return {
unixMillisec: new Date().getTime(),
id: CommandId(uuid4()),
redoPatches: doPatches,
undoPatches: undoPatches,
};
Expand Down Expand Up @@ -133,30 +134,17 @@ export const commandStore = createPartialStore<CommandStoreTypes>({
},
},

LAST_COMMAND_UNIX_MILLISEC: {
LAST_COMMAND_IDS: {
getter(state) {
const getLastCommandUnixMillisec = (
commands: Command[],
): number | null => {
const lastCommand = commands[commands.length - 1];
// 型的にはundefinedにはならないが、lengthが0の場合はundefinedになる
return lastCommand ? lastCommand.unixMillisec : null;
const getLastCommandId = (commands: Command[]): CommandId | null => {
if (commands.length == 0) return null;
else return commands[commands.length - 1].id;
};

const lastTalkCommandTime = getLastCommandUnixMillisec(
state.undoCommands["talk"],
);
const lastSongCommandTime = getLastCommandUnixMillisec(
state.undoCommands["song"],
);

if (lastTalkCommandTime != null && lastSongCommandTime != null) {
return Math.max(lastTalkCommandTime, lastSongCommandTime);
} else if (lastTalkCommandTime != null) {
return lastTalkCommandTime;
} else {
return lastSongCommandTime;
}
return {
talk: getLastCommandId(state.undoCommands["talk"]),
song: getLastCommandId(state.undoCommands["song"]),
};
},
},

Expand Down
3 changes: 2 additions & 1 deletion src/store/preset.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { createPartialStore } from "./vuex";
import { uuid4 } from "@/helpers/random";
import { PresetStoreState, PresetStoreTypes, State } from "@/store/type";
import { Preset, PresetKey, Voice, VoiceId } from "@/type/preload";

Expand Down Expand Up @@ -181,7 +182,7 @@ export const presetStore = createPartialStore<PresetStoreTypes>({

ADD_PRESET: {
async action(context, { presetData }: { presetData: Preset }) {
const newKey = PresetKey(crypto.randomUUID());
const newKey = PresetKey(uuid4());
const newPresetItems = {
...context.state.presetItems,
[newKey]: presetData,
Expand Down
39 changes: 27 additions & 12 deletions src/store/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,11 @@ import {
createDefaultTimeSignature,
DEFAULT_TPQN,
} from "@/sing/domain";
import { EditorType } from "@/type/preload";
import { IsEqual } from "@/type/utility";

export const projectStoreState: ProjectStoreState = {
savedLastCommandUnixMillisec: null,
savedLastCommandIds: { talk: null, song: null },
};

const applyTalkProjectToStore = async (
Expand Down Expand Up @@ -132,7 +134,7 @@ export const projectStore = createPartialStore<ProjectStoreTypes>({
await context.dispatch("CLEAR_PITCH_EDIT_DATA");

context.commit("SET_PROJECT_FILEPATH", { filePath: undefined });
context.commit("SET_SAVED_LAST_COMMAND_UNIX_MILLISEC", null);
context.commit("RESET_SAVED_LAST_COMMAND_IDS");
context.commit("CLEAR_COMMANDS");
},
),
Expand Down Expand Up @@ -211,7 +213,7 @@ export const projectStore = createPartialStore<ProjectStoreTypes>({
await applySongProjectToStore(dispatch, parsedProjectData.song);

commit("SET_PROJECT_FILEPATH", { filePath });
commit("SET_SAVED_LAST_COMMAND_UNIX_MILLISEC", null);
commit("RESET_SAVED_LAST_COMMAND_IDS");
commit("CLEAR_COMMANDS");
return true;
} catch (err) {
Expand Down Expand Up @@ -314,8 +316,8 @@ export const projectStore = createPartialStore<ProjectStoreTypes>({
.then(getValueOrThrow);
context.commit("SET_PROJECT_FILEPATH", { filePath });
context.commit(
"SET_SAVED_LAST_COMMAND_UNIX_MILLISEC",
context.getters.LAST_COMMAND_UNIX_MILLISEC,
"SET_SAVED_LAST_COMMAND_IDS",
context.getters.LAST_COMMAND_IDS,
);
return true;
} catch (err) {
Expand Down Expand Up @@ -372,16 +374,29 @@ export const projectStore = createPartialStore<ProjectStoreTypes>({

IS_EDITED: {
getter(state, getters) {
return (
getters.LAST_COMMAND_UNIX_MILLISEC !==
state.savedLastCommandUnixMillisec
);
// eslint-disable-next-line @typescript-eslint/no-unused-vars
Hiroshiba marked this conversation as resolved.
Show resolved Hide resolved
const _: IsEqual<
typeof state.savedLastCommandIds,
typeof getters.LAST_COMMAND_IDS
> = true;
return Object.keys(state.savedLastCommandIds).some((_editor) => {
const editor = _editor as EditorType;
return (
state.savedLastCommandIds[editor] !== getters.LAST_COMMAND_IDS[editor]
);
});
},
},

SET_SAVED_LAST_COMMAND_IDS: {
mutation(state, commandIds) {
state.savedLastCommandIds = commandIds;
},
},

SET_SAVED_LAST_COMMAND_UNIX_MILLISEC: {
mutation(state, unixMillisec) {
state.savedLastCommandUnixMillisec = unixMillisec;
RESET_SAVED_LAST_COMMAND_IDS: {
mutation(state) {
state.savedLastCommandIds = { talk: null, song: null };
},
},
});
9 changes: 5 additions & 4 deletions src/store/singing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ import { createLogger } from "@/domain/frontend/log";
import { noteSchema } from "@/domain/project/schema";
import { getOrThrow } from "@/helpers/mapHelper";
import { ufProjectToVoicevox } from "@/sing/utaformatixProject/toVoicevox";
import { uuid4 } from "@/helpers/random";

const logger = createLogger("store/singing");

Expand Down Expand Up @@ -1708,7 +1709,7 @@ export const singingStore = createPartialStore<SingingStoreTypes>({
await dispatch("SET_TIME_SIGNATURES", { timeSignatures });
await dispatch("SET_NOTES", { notes });

commit("SET_SAVED_LAST_COMMAND_UNIX_MILLISEC", null);
commit("RESET_SAVED_LAST_COMMAND_IDS");
commit("CLEAR_COMMANDS");
dispatch("RENDER");
},
Expand All @@ -1724,7 +1725,7 @@ export const singingStore = createPartialStore<SingingStoreTypes>({
const track = tracks[trackIndex];
const notes = track.notes.map((note) => ({
...note,
id: NoteId(crypto.randomUUID()),
id: NoteId(uuid4()),
}));

if (tpqn !== state.tpqn) {
Expand All @@ -1751,7 +1752,7 @@ export const singingStore = createPartialStore<SingingStoreTypes>({
startFrame: 0,
});

commit("SET_SAVED_LAST_COMMAND_UNIX_MILLISEC", null);
commit("RESET_SAVED_LAST_COMMAND_IDS");
commit("CLEAR_COMMANDS");
dispatch("RENDER");
},
Expand Down Expand Up @@ -2144,7 +2145,7 @@ export const singingStore = createPartialStore<SingingStoreTypes>({
const quantizedPastePos =
Math.round(pasteOriginPos / snapTicks) * snapTicks;
return {
id: NoteId(crypto.randomUUID()),
id: NoteId(uuid4()),
position: quantizedPastePos,
duration: Number(note.duration),
noteNumber: Number(note.noteNumber),
Expand Down
17 changes: 11 additions & 6 deletions src/store/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ import {
RootMiscSettingType,
EditorType,
NoteId,
CommandId,
} from "@/type/preload";
import { IEngineConnectorFactory } from "@/infrastructures/EngineConnector";
import {
Expand Down Expand Up @@ -94,7 +95,7 @@ export type FetchAudioResult = {
};

export type Command = {
unixMillisec: number;
id: CommandId;
undoPatches: Patch[];
redoPatches: Patch[];
};
Expand Down Expand Up @@ -1233,8 +1234,8 @@ export type CommandStoreTypes = {
action(payload: { editor: EditorType }): void;
};

LAST_COMMAND_UNIX_MILLISEC: {
getter: number | null;
LAST_COMMAND_IDS: {
getter: Record<EditorType, CommandId | null>;
};

CLEAR_COMMANDS: {
Expand Down Expand Up @@ -1469,7 +1470,7 @@ export type IndexStoreTypes = {

export type ProjectStoreState = {
projectFilePath?: string;
savedLastCommandUnixMillisec: number | null;
savedLastCommandIds: Record<EditorType, CommandId | null>;
};

export type ProjectStoreTypes = {
Expand Down Expand Up @@ -1511,8 +1512,12 @@ export type ProjectStoreTypes = {
getter: boolean;
};

SET_SAVED_LAST_COMMAND_UNIX_MILLISEC: {
mutation: number | null;
SET_SAVED_LAST_COMMAND_IDS: {
mutation: Record<EditorType, CommandId | null>;
};

RESET_SAVED_LAST_COMMAND_IDS: {
mutation: void;
};
};

Expand Down
4 changes: 4 additions & 0 deletions src/type/preload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@ export const noteIdSchema = z.string().brand<"NoteId">();
export type NoteId = z.infer<typeof noteIdSchema>;
export const NoteId = (id: string): NoteId => noteIdSchema.parse(id);

export const commandIdSchema = z.string().brand<"CommandId">();
export type CommandId = z.infer<typeof commandIdSchema>;
export const CommandId = (id: string): CommandId => commandIdSchema.parse(id);

// 共通のアクション名
export const actionPostfixSelectNthCharacter = "番目のキャラクターを選択";

Expand Down
3 changes: 2 additions & 1 deletion tests/unit/lib/selectPriorPhrase.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
} from "@/store/type";
import { DEFAULT_TPQN, selectPriorPhrase } from "@/sing/domain";
import { NoteId } from "@/type/preload";
import { uuid4 } from "@/helpers/random";

const createPhrase = (
firstRestDuration: number,
Expand All @@ -18,7 +19,7 @@ const createPhrase = (
firstRestDuration: firstRestDuration * DEFAULT_TPQN,
notes: [
{
id: NoteId(crypto.randomUUID()),
id: NoteId(uuid4()),
position: start * DEFAULT_TPQN,
duration: (end - start) * DEFAULT_TPQN,
noteNumber: 60,
Expand Down
3 changes: 2 additions & 1 deletion tests/unit/lib/utaformatixProject/export.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ import {
createDefaultTrack,
} from "@/sing/domain";
import { NoteId } from "@/type/preload";
import { uuid4 } from "@/helpers/random";

const createNoteId = () => NoteId(crypto.randomUUID());
const createNoteId = () => NoteId(uuid4());

it("トラックを変換できる", async () => {
const track = createDefaultTrack();
Expand Down
Loading
Loading