diff --git a/Backend/Models/Sense.cs b/Backend/Models/Sense.cs
index 4f9e09991d..e84faac50e 100644
--- a/Backend/Models/Sense.cs
+++ b/Backend/Models/Sense.cs
@@ -300,7 +300,6 @@ public enum Status
Active,
Deleted,
Duplicate,
- Protected,
- Separate
+ Protected
}
}
diff --git a/src/api/models/status.ts b/src/api/models/status.ts
index a60dbb7fc8..88fd8874f1 100644
--- a/src/api/models/status.ts
+++ b/src/api/models/status.ts
@@ -22,5 +22,4 @@ export enum Status {
Deleted = "Deleted",
Duplicate = "Duplicate",
Protected = "Protected",
- Separate = "Separate",
}
diff --git a/src/goals/MergeDuplicates/MergeDupsStep/MergeDragDrop/DropWord.tsx b/src/goals/MergeDuplicates/MergeDupsStep/MergeDragDrop/DropWord.tsx
index ba67568825..1dfe454a74 100644
--- a/src/goals/MergeDuplicates/MergeDupsStep/MergeDragDrop/DropWord.tsx
+++ b/src/goals/MergeDuplicates/MergeDupsStep/MergeDragDrop/DropWord.tsx
@@ -7,24 +7,25 @@ import {
Select,
Typography,
} from "@mui/material";
-import { ReactElement } from "react";
+import { type ReactElement } from "react";
import { Droppable } from "react-beautiful-dnd";
import { useTranslation } from "react-i18next";
-import { Flag, ProtectReason, ReasonType } from "api/models";
+import { type Flag, type ProtectReason, ReasonType } from "api/models";
import {
FlagButton,
IconButtonWithTooltip,
NoteButton,
} from "components/Buttons";
import MultilineTooltipTitle from "components/MultilineTooltipTitle";
+import { AudioSummary } from "components/WordCard";
import DragSense from "goals/MergeDuplicates/MergeDupsStep/MergeDragDrop/DragSense";
-import { MergeTreeWord } from "goals/MergeDuplicates/MergeDupsTreeTypes";
+import { type MergeTreeWord } from "goals/MergeDuplicates/MergeDupsTreeTypes";
import {
flagWord,
setVern,
} from "goals/MergeDuplicates/Redux/MergeDupsActions";
-import { StoreState } from "types";
+import { type StoreState } from "types";
import { useAppDispatch, useAppSelector } from "types/hooks";
import theme from "types/theme";
import { TypographyWithFont } from "utilities/fontComponents";
@@ -110,6 +111,9 @@ export function DropWordCardHeader(
const { senses, words } = useAppSelector(
(state: StoreState) => state.mergeDuplicateGoal.data
);
+ const { counts, moves } = useAppSelector(
+ (state: StoreState) => state.mergeDuplicateGoal.audio
+ );
const { t } = useTranslation();
@@ -126,6 +130,11 @@ export function DropWordCardHeader(
...new Set(guids.map((g) => words[senses[g].srcWordId].vernacular)),
];
+ // Compute how many audio pronunciations the word will have post-merge.
+ const otherIds = moves[props.wordId] ?? [];
+ const otherCount = otherIds.reduce((sum, id) => sum + counts[id], 0);
+ const audioCount = (treeWord?.audioCount ?? 0) + otherCount;
+
// Reset vern if not in vern list.
if (treeWord && !verns.includes(treeWord.vern)) {
dispatchSetVern(verns.length ? verns[0] : "");
@@ -231,6 +240,7 @@ export function DropWordCardHeader(
text={}
/>
)}
+
{treeWord.note.text ? : null}
({}));
jest.mock("goals/MergeDuplicates/Redux/MergeDupsActions", () => ({
setSidebar: (...args: any[]) => mockSetSidebar(...args),
}));
+// Mock "i18n", else `Error: connect ECONNREFUSED ::1:80`
+jest.mock("i18n", () => ({}));
jest.mock("types/hooks", () => {
return {
...jest.requireActual("types/hooks"),
diff --git a/src/goals/MergeDuplicates/MergeDupsTreeTypes.ts b/src/goals/MergeDuplicates/MergeDupsTreeTypes.ts
index 23958bf6f9..c27d7a0fcd 100644
--- a/src/goals/MergeDuplicates/MergeDupsTreeTypes.ts
+++ b/src/goals/MergeDuplicates/MergeDupsTreeTypes.ts
@@ -37,6 +37,7 @@ export interface MergeTreeWord {
flag: Flag;
note: Note;
protected: boolean;
+ audioCount: number;
}
export function newMergeTreeSense(
@@ -63,6 +64,7 @@ export function newMergeTreeWord(
flag: newFlag(),
note: newNote(),
protected: false,
+ audioCount: 0,
};
}
@@ -87,6 +89,7 @@ export function convertWordToMergeTreeWord(word: Word): MergeTreeWord {
mergeTreeWord.flag = { ...word.flag };
mergeTreeWord.note = { ...word.note };
mergeTreeWord.protected = word.accessibility === Status.Protected;
+ mergeTreeWord.audioCount = word.audio.length;
return mergeTreeWord;
}
diff --git a/src/goals/MergeDuplicates/Redux/MergeDupsReducer.ts b/src/goals/MergeDuplicates/Redux/MergeDupsReducer.ts
index e7a5332228..e498611df1 100644
--- a/src/goals/MergeDuplicates/Redux/MergeDupsReducer.ts
+++ b/src/goals/MergeDuplicates/Redux/MergeDupsReducer.ts
@@ -14,11 +14,17 @@ import {
defaultTree,
newMergeTreeWord,
} from "goals/MergeDuplicates/MergeDupsTreeTypes";
-import { newMergeWords } from "goals/MergeDuplicates/MergeDupsTypes";
-import { defaultState } from "goals/MergeDuplicates/Redux/MergeDupsReduxTypes";
import {
- buildSenses,
- createMergeWords,
+ defaultAudio,
+ defaultState,
+} from "goals/MergeDuplicates/Redux/MergeDupsReduxTypes";
+import {
+ combineIntoFirstSense,
+ createMergeChildren,
+ createMergeParent,
+ gatherWordSenses,
+ getDeletedMergeWords,
+ isEmptyMerge,
} from "goals/MergeDuplicates/Redux/reducerUtilities";
import { StoreActionTypes } from "rootActions";
import { type Hash } from "types/hash";
@@ -80,6 +86,7 @@ const mergeDuplicatesSlice = createSlice({
deletedSenseGuids.push(...srcGuids);
delete sensesGuids[srcRef.mergeSenseId];
if (!Object.keys(sensesGuids).length) {
+ delete state.audio.moves[srcWordId];
delete words[srcWordId];
}
@@ -104,32 +111,51 @@ const mergeDuplicatesSlice = createSlice({
},
getMergeWordsAction: (state) => {
- // Handle words with all senses deleted.
- const possibleWords = Object.values(state.data.words);
+ const dataWords = Object.values(state.data.words);
const deletedSenseGuids = state.tree.deletedSenseGuids;
- const deletedWords = possibleWords.filter((w) =>
- w.senses.every((s) => deletedSenseGuids.includes(s.guid))
- );
- state.mergeWords = deletedWords.map((w) =>
- newMergeWords(w, [{ srcWordId: w.id, getAudio: false }], true)
+
+ // First handle words with all senses deleted.
+ state.mergeWords = getDeletedMergeWords(dataWords, deletedSenseGuids);
+
+ // Then build the rest of the mergeWords.
+
+ // Gather all senses (accessibility will be updated as mergeWords are built).
+ const wordTreeSenses = gatherWordSenses(dataWords, deletedSenseGuids);
+ const allSenses = Object.values(wordTreeSenses).flatMap((mergeSenses) =>
+ mergeSenses.map((ms) => ms.sense)
);
+ // Build one merge word per column.
for (const wordId in state.tree.words) {
+ // Get from tree the basic info for this column.
const mergeWord = state.tree.words[wordId];
- const mergeSenses = buildSenses(
- mergeWord.sensesGuids,
- state.data,
- deletedSenseGuids
+
+ // Get from data all senses in this column.
+ const mergeSenses = Object.values(mergeWord.sensesGuids).map((guids) =>
+ guids.map((g) => state.data.senses[g])
);
- const mergeWords = createMergeWords(
- wordId,
- mergeWord,
+
+ // Update those senses in the set of all senses.
+ mergeSenses.forEach((senses) => {
+ const sensesToUpdate = senses.map(
+ (s) => wordTreeSenses[s.srcWordId][s.order]
+ );
+ combineIntoFirstSense(sensesToUpdate);
+ });
+
+ // Check if nothing to merge.
+ const wordToUpdate = state.data.words[wordId];
+ if (isEmptyMerge(wordToUpdate, mergeWord)) {
+ continue;
+ }
+
+ // Create merge words.
+ const children = createMergeChildren(
mergeSenses,
- state.data.words[wordId]
+ state.audio.moves[wordId]
);
- if (mergeWords) {
- state.mergeWords.push(mergeWords);
- }
+ const parent = createMergeParent(wordToUpdate, mergeWord, allSenses);
+ state.mergeWords.push({ parent, children, deleteOnly: false });
}
},
@@ -161,6 +187,17 @@ const mergeDuplicatesSlice = createSlice({
// Cleanup the srcWord.
delete words[srcWordId].sensesGuids[mergeSenseId];
if (!Object.keys(words[srcWordId].sensesGuids).length) {
+ // If this was the word's last sense, move the audio...
+ const moves = state.audio.moves;
+ if (!Object.keys(moves).includes(destWordId)) {
+ moves[destWordId] = [];
+ }
+ moves[destWordId].push(srcWordId);
+ if (Object.keys(moves).includes(srcWordId)) {
+ moves[destWordId].push(...moves[srcWordId]);
+ delete moves[srcWordId];
+ }
+ // ...and delete the word from the tree
delete words[srcWordId];
}
}
@@ -254,15 +291,18 @@ const mergeDuplicatesSlice = createSlice({
const words: Hash = {};
const senses: Hash = {};
const wordsTree: Hash = {};
+ const counts: Hash = {};
action.payload.forEach((word: Word) => {
words[word.id] = JSON.parse(JSON.stringify(word));
word.senses.forEach((s, order) => {
senses[s.guid] = convertSenseToMergeTreeSense(s, word.id, order);
});
wordsTree[word.id] = convertWordToMergeTreeWord(word);
+ counts[word.id] = word.audio.length;
});
state.data = { ...defaultData, senses, words };
state.tree = { ...defaultTree, words: wordsTree };
+ state.audio = { ...defaultAudio, counts };
state.mergeWords = [];
}
},
diff --git a/src/goals/MergeDuplicates/Redux/MergeDupsReduxTypes.ts b/src/goals/MergeDuplicates/Redux/MergeDupsReduxTypes.ts
index 3633741662..95663ebb2e 100644
--- a/src/goals/MergeDuplicates/Redux/MergeDupsReduxTypes.ts
+++ b/src/goals/MergeDuplicates/Redux/MergeDupsReduxTypes.ts
@@ -6,18 +6,38 @@ import {
defaultData,
defaultTree,
} from "goals/MergeDuplicates/MergeDupsTreeTypes";
+import { type Hash } from "types/hash";
// Redux state
+/** `.counts` is a dictionary of all audio counts of the words being merged:
+ * - key: id of a word in the set of potential duplicates
+ * - value: number of audio pronunciations on the word
+ *
+ * `.moves` is a dictionary of words receiving the audio of other words:
+ * - key: id of a word receiving audio
+ * - value: array of ids of words whose audio is being received */
+export interface MergeAudio {
+ counts: Hash;
+ moves: Hash;
+}
+
+export const defaultAudio: MergeAudio = {
+ counts: {},
+ moves: {},
+};
+
export interface MergeTreeState {
data: MergeData;
tree: MergeTree;
+ audio: MergeAudio;
mergeWords: MergeWords[];
}
export const defaultState: MergeTreeState = {
data: defaultData,
tree: defaultTree,
+ audio: defaultAudio,
mergeWords: [],
};
diff --git a/src/goals/MergeDuplicates/Redux/reducerUtilities.ts b/src/goals/MergeDuplicates/Redux/reducerUtilities.ts
index 1faa00b74c..0fc98d95d4 100644
--- a/src/goals/MergeDuplicates/Redux/reducerUtilities.ts
+++ b/src/goals/MergeDuplicates/Redux/reducerUtilities.ts
@@ -2,11 +2,11 @@ import {
GramCatGroup,
type MergeSourceWord,
type MergeWords,
+ type Sense,
Status,
type Word,
} from "api/models";
import {
- type MergeData,
type MergeTreeSense,
type MergeTreeWord,
} from "goals/MergeDuplicates/MergeDupsTreeTypes";
@@ -16,121 +16,102 @@ import { compareFlags } from "utilities/wordUtilities";
// A collection of helper/utility functions only for use in the MergeDupsReducer.
-/** Create hash of senses keyed by id of src word. */
-export function buildSenses(
- sensesGuids: Hash,
- data: MergeData,
+/** Generate dictionary of MergeTreeSense arrays:
+ * - key: word id
+ * - value: all merge senses of the word */
+export function gatherWordSenses(
+ words: Word[],
deletedSenseGuids: string[]
): Hash {
- const senses: Hash = {};
- for (const senseGuids of Object.values(sensesGuids)) {
- for (const guid of senseGuids) {
- const senseData = data.senses[guid];
- const wordId = senseData.srcWordId;
-
- if (!senses[wordId]) {
- const dbWord = data.words[wordId];
+ return Object.fromEntries(
+ words.map((w) => [w.id, gatherSenses(w, deletedSenseGuids)])
+ );
+}
- // Add each sense into senses as separate or deleted.
- senses[wordId] = [];
- for (const sense of dbWord.senses) {
- senses[wordId].push({
- order: senses[wordId].length,
- protected: sense.accessibility === Status.Protected,
- srcWordId: wordId,
- sense: {
- ...sense,
- accessibility: deletedSenseGuids.includes(sense.guid)
- ? Status.Deleted
- : Status.Separate,
- },
- });
- }
- }
- }
- }
+/** Generate MergeTreeSense array with deleted senses set to Status.Deleted. */
+function gatherSenses(
+ word: Word,
+ deletedSenseGuids: string[]
+): MergeTreeSense[] {
+ return word.senses.map((sense, index) => ({
+ order: index,
+ protected: sense.accessibility === Status.Protected,
+ srcWordId: word.id,
+ sense: {
+ ...sense,
+ accessibility: deletedSenseGuids.includes(sense.guid)
+ ? Status.Deleted
+ : sense.accessibility,
+ },
+ }));
+}
- // Set sense and duplicate senses.
- Object.values(sensesGuids).forEach((guids) => {
- const sensesToCombine = guids
- .map((g) => data.senses[g])
- .map((s) => senses[s.srcWordId][s.order]);
- combineIntoFirstSense(sensesToCombine);
- });
+/** Create a MergeWords array for the words which had all senses deleted. */
+export function getDeletedMergeWords(
+ words: Word[],
+ deletedSenseGuids: string[]
+): MergeWords[] {
+ return words
+ .filter((w) => w.senses.every((s) => deletedSenseGuids.includes(s.guid)))
+ .map((w) => newMergeWords(w, [{ srcWordId: w.id, getAudio: false }], true));
+}
- // Clean order of senses in each src word to reflect backend order.
- Object.values(senses).forEach((wordSenses) => {
- wordSenses = wordSenses.sort((a, b) => a.order - b.order);
- senses[wordSenses[0].srcWordId] = wordSenses;
- });
+/** Determine if a merge is empty:
+ * - no senses have been merged in as duplicates
+ * - the sense guids all match
+ * - the flag is unchanged */
+export function isEmptyMerge(word: Word, mergeWord: MergeTreeWord): boolean {
+ const mergeSensesGuids = Object.values(mergeWord.sensesGuids);
+ const mergeGuids = mergeSensesGuids.map((guids) => guids[0]);
+ const wordSenseGuids = word.senses.map((s) => s.guid);
+ return (
+ mergeSensesGuids.every((guids) => guids.length === 1) &&
+ mergeGuids.length === wordSenseGuids.length &&
+ wordSenseGuids.every((guid) => mergeGuids.includes(guid)) &&
+ compareFlags(mergeWord.flag, word.flag) === 0
+ );
+}
- return senses;
+/** Construct children of a MergeWord. */
+export function createMergeChildren(
+ mergeSenses: MergeTreeSense[][],
+ audioMoves: string[] = []
+): MergeSourceWord[] {
+ const redundantIds = mergeSenses.flatMap((senses) =>
+ senses.map((s) => s.srcWordId)
+ );
+ const childrenIds = [...new Set(redundantIds)];
+ return childrenIds.map((srcWordId) => ({
+ srcWordId,
+ getAudio: audioMoves.includes(srcWordId),
+ }));
}
-export function createMergeWords(
- wordId: string,
+/** Construct parent of a MergeWord. */
+export function createMergeParent(
+ word: Word,
mergeWord: MergeTreeWord,
- mergeSenses: Hash,
- word: Word
-): MergeWords | undefined {
- // Don't return empty merges: when the only child is the parent word
- // and has the same number of senses as parent (all Active/Protected)
- // and has the same flag.
- if (Object.values(mergeSenses).length === 1) {
- const onlyChild = Object.values(mergeSenses)[0];
- if (
- onlyChild[0].srcWordId === wordId &&
- onlyChild.length === word.senses.length &&
- onlyChild.every((ms) =>
- [Status.Active, Status.Protected].includes(ms.sense.accessibility)
- ) &&
- compareFlags(mergeWord.flag, word.flag) === 0
- ) {
- return;
- }
- }
-
- // Construct parent and children.
- const parent: Word = {
+ allSenses: Sense[]
+): Word {
+ // Construct parent.
+ const senses = Object.values(mergeWord.sensesGuids)
+ .map((guids) => guids[0])
+ .map((g) => allSenses.find((s) => s.guid === g)!);
+ return {
...word,
- senses: [],
flag: mergeWord.flag,
+ senses,
+ vernacular: mergeWord.vern.trim() || word.vernacular.trim(),
};
- if (!parent.vernacular) {
- parent.vernacular = mergeWord.vern;
- }
- const children: MergeSourceWord[] = Object.values(mergeSenses).map(
- (msList) => {
- msList.forEach((mergeSense) => {
- if (
- [Status.Active, Status.Protected].includes(
- mergeSense.sense.accessibility
- )
- ) {
- parent.senses.push(mergeSense.sense);
- }
- });
- const getAudio = msList.every(
- (ms) => ms.sense.accessibility !== Status.Separate
- );
- return { srcWordId: msList[0].srcWordId, getAudio };
- }
- );
-
- return newMergeWords(parent, children);
}
/** Given an array of senses to combine:
- * - change the accessibility of the first one from Separate to Active/Protected,
- * - change the accessibility of the rest to Duplicate,
- * - merge select content from duplicates into main sense */
-function combineIntoFirstSense(mergeSenses: MergeTreeSense[]): void {
- // Set the first sense to be merged as Active/Protected.
- // This was the top sense when the sidebar was opened.
+ * - identify the first/top sense as the main one to keep;
+ * - change the accessibility of the rest to Status.Duplicate;
+ * - merge select content from the rest into main sense */
+export function combineIntoFirstSense(mergeSenses: MergeTreeSense[]): void {
+ // Set the main sense to the first sense (the top one when the sidebar was opened).
const mainSense = mergeSenses[0].sense;
- mainSense.accessibility = mergeSenses[0].protected
- ? Status.Protected
- : Status.Active;
// Merge the rest as duplicates.
// These were senses dropped into another sense.
diff --git a/src/goals/MergeDuplicates/Redux/tests/MergeDupsActions.test.tsx b/src/goals/MergeDuplicates/Redux/tests/MergeDupsActions.test.tsx
index 7aed209a05..0905887da3 100644
--- a/src/goals/MergeDuplicates/Redux/tests/MergeDupsActions.test.tsx
+++ b/src/goals/MergeDuplicates/Redux/tests/MergeDupsActions.test.tsx
@@ -1,4 +1,10 @@
-import { type MergeWords, type Sense, Status, type Word } from "api/models";
+import {
+ type MergeSourceWord,
+ type MergeWords,
+ type Sense,
+ Status,
+ type Word,
+} from "api/models";
import { defaultState } from "components/App/DefaultState";
import {
type MergeData,
@@ -14,7 +20,10 @@ import {
mergeAll,
setData,
} from "goals/MergeDuplicates/Redux/MergeDupsActions";
-import { defaultState as defaultMergeState } from "goals/MergeDuplicates/Redux/MergeDupsReduxTypes";
+import {
+ defaultAudio,
+ defaultState as defaultMergeState,
+} from "goals/MergeDuplicates/Redux/MergeDupsReduxTypes";
import { goalDataMock } from "goals/MergeDuplicates/Redux/tests/MergeDupsDataMock";
import { setupStore } from "store";
import { GoalType } from "types/goals";
@@ -42,6 +51,13 @@ jest.mock("backend", () => ({
mockMergeWords(mergeWordsArray),
}));
+function newMergeSourceWord(
+ srcWordId: string,
+ getAudio = false
+): MergeSourceWord {
+ return { srcWordId, getAudio };
+}
+
const mockGoal = new MergeDups();
mockGoal.data = goalDataMock;
mockGoal.steps = [{ words: [] }, { words: [] }];
@@ -133,8 +149,8 @@ describe("MergeDupActions", () => {
expect(mockMergeWords).toHaveBeenCalledTimes(1);
const parentA = wordAnyGuids(vernA, [senses["S1"], senses["S2"]], idA);
const parentB = wordAnyGuids(vernB, [senses["S4"]], idB);
- const childA = { srcWordId: idA, getAudio: true };
- const childB = { srcWordId: idB, getAudio: false };
+ const childA = newMergeSourceWord(idA);
+ const childB = newMergeSourceWord(idB);
const mockMerges = [
newMergeWords(parentA, [childA, childB]),
newMergeWords(parentB, [childB]),
@@ -150,9 +166,9 @@ describe("MergeDupActions", () => {
expect(blacklist).not.toContain(idB);
});
- // Move sense 3 from B to A
+ // Move sense 3 from B to middle sense in A
it("moves sense between words", async () => {
- const WA = newMergeTreeWord(vernA, { ID1: [S1], ID2: [S2], ID3: [S3] });
+ const WA = newMergeTreeWord(vernA, { ID1: [S1], ID2: [S3], ID3: [S2] });
const WB = newMergeTreeWord(vernB, { ID1: [S4] });
const tree: MergeTree = { ...defaultTree, words: { WA, WB } };
const store = setupStore({
@@ -164,12 +180,12 @@ describe("MergeDupActions", () => {
expect(mockMergeWords).toHaveBeenCalledTimes(1);
const parentA = wordAnyGuids(
vernA,
- [senses["S1"], senses["S2"], senses["S3"]],
+ [senses["S1"], senses["S3"], senses["S2"]],
idA
);
const parentB = wordAnyGuids(vernB, [senses["S4"]], idB);
- const childA = { srcWordId: idA, getAudio: true };
- const childB = { srcWordId: idB, getAudio: false };
+ const childA = newMergeSourceWord(idA);
+ const childB = newMergeSourceWord(idB);
const mockMerges = [
newMergeWords(parentA, [childA, childB]),
newMergeWords(parentB, [childB]),
@@ -199,7 +215,7 @@ describe("MergeDupActions", () => {
expect(mockMergeWords).toHaveBeenCalledTimes(1);
const parent = wordAnyGuids(vernA, [senses["S1"]], idA);
- const child = { srcWordId: idA, getAudio: true };
+ const child = newMergeSourceWord(idA);
const mockMerge = newMergeWords(parent, [child]);
expect(mockMergeWords).toHaveBeenCalledWith([mockMerge]);
@@ -227,7 +243,7 @@ describe("MergeDupActions", () => {
expect(mockMergeWords).toHaveBeenCalledTimes(1);
const parent = wordAnyGuids(vernA, [senses["S1"]], idA);
- const child = { srcWordId: idA, getAudio: true };
+ const child = newMergeSourceWord(idA);
const mockMerge = newMergeWords(parent, [child]);
expect(mockMergeWords).toHaveBeenCalledWith([mockMerge]);
@@ -253,13 +269,14 @@ describe("MergeDupActions", () => {
await store.dispatch(mergeAll());
expect(mockMergeWords).toHaveBeenCalledTimes(1);
- const child = { srcWordId: idB, getAudio: false };
+ const child = newMergeSourceWord(idB);
const mockMerge = newMergeWords(wordB, [child], true);
expect(mockMergeWords).toHaveBeenCalledWith([mockMerge]);
// No blacklist entry added for only 1 resulting word.
expect(mockBlacklistAdd).not.toHaveBeenCalled();
});
+
// Move all senses from B to A
it("moves all senses to other word", async () => {
const WA = newMergeTreeWord(vernA, {
@@ -270,7 +287,12 @@ describe("MergeDupActions", () => {
const tree: MergeTree = { ...defaultTree, words: { WA } };
const store = setupStore({
...preloadedState,
- mergeDuplicateGoal: { ...defaultMergeState, data, tree },
+ mergeDuplicateGoal: {
+ ...defaultMergeState,
+ data,
+ tree,
+ audio: { ...defaultAudio, moves: { [idA]: [idB] } },
+ },
});
await store.dispatch(mergeAll());
@@ -280,8 +302,8 @@ describe("MergeDupActions", () => {
[senses["S1"], senses["S2"], senses["S4"]],
idA
);
- const childA = { srcWordId: idA, getAudio: true };
- const childB = { srcWordId: idB, getAudio: true };
+ const childA = newMergeSourceWord(idA);
+ const childB = newMergeSourceWord(idB, true);
const mockMerge = newMergeWords(parentA, [childA, childB]);
expect(mockMergeWords).toHaveBeenCalledWith([mockMerge]);
@@ -305,7 +327,7 @@ describe("MergeDupActions", () => {
const parent = wordAnyGuids(vernA, [senses["S1"], senses["S2"]], idA);
parent.flag = WA.flag;
- const child = { srcWordId: idA, getAudio: true };
+ const child = newMergeSourceWord(idA);
const mockMerge = newMergeWords(parent, [child]);
expect(mockMergeWords).toHaveBeenCalledWith([mockMerge]);