Skip to content

Commit

Permalink
Add tests and tidy code
Browse files Browse the repository at this point in the history
  • Loading branch information
imnasnainaec committed Oct 20, 2023
1 parent e24a044 commit 63447cf
Show file tree
Hide file tree
Showing 11 changed files with 128 additions and 74 deletions.
35 changes: 35 additions & 0 deletions Backend.Tests/Controllers/WordControllerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -397,5 +397,40 @@ public async Task TestUpdateWordMissingIds()
var wordResult = await _wordController.UpdateWord(_projId, MissingId, modWord);
Assert.That(wordResult, Is.InstanceOf<NotFoundObjectResult>());
}

[Test]
public async Task TestGetWordHistoryNoPermission()
{
_wordController.ControllerContext.HttpContext = PermissionServiceMock.UnauthorizedHttpContext();

var origWord = await _wordRepo.Create(Util.RandomWord(_projId));
var result = await _wordController.GetWordHistory(_projId, origWord.Id);
Assert.That(result, Is.InstanceOf<ForbidResult>());
}

[Test]
public async Task TestGetWordHistoryMissingIds()
{
var origWord = await _wordRepo.Create(Util.RandomWord(_projId));
var projectResult = await _wordController.GetWordHistory(MissingId, origWord.Id);
Assert.That(projectResult, Is.InstanceOf<NotFoundObjectResult>());

var wordResult = await _wordController.GetWordHistory(_projId, MissingId);
Assert.That(wordResult, Is.InstanceOf<NotFoundObjectResult>());
}

[Test]
public async Task TestGetWordHistoryParentCount()
{
var father = await _wordRepo.Create(Util.RandomWord(_projId));
var mother = await _wordRepo.Create(Util.RandomWord(_projId));
var word = Util.RandomWord(_projId);
word.History = new List<string> { father.Id, mother.Id };
word = await _wordRepo.Create(word);
var result = await _wordController.GetWordHistory(_projId, word.Id);
Assert.That(result, Is.InstanceOf<OkObjectResult>());
var pedigree = (Pedigree)((OkObjectResult)result).Value!;
Assert.That(pedigree.Parents, Has.Count.EqualTo(2));
}
}
}
2 changes: 1 addition & 1 deletion Backend/Interfaces/IWordService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@ public interface IWordService
Task<string?> FindContainingWord(Word word);
Task<Word?> Delete(string projectId, string wordId, string fileName);
Task<string?> DeleteFrontierWord(string projectId, string wordId);
Task<Pedigree> GeneratePedigree(string projId, Word word);
Task<Pedigree> GeneratePedigree(string projectId, Word word);
}
}
6 changes: 3 additions & 3 deletions Backend/Services/WordService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ public async Task<bool> Update(string projectId, string wordId, Word word)
}

/// <summary> Builds a <see cref="Pedigree"/> from a <see cref="Word"/>'s history. </summary>
public async Task<Pedigree> GeneratePedigree(string projId, Word word)
public async Task<Pedigree> GeneratePedigree(string projectId, Word word)
{
var tree = new Pedigree(word);
// Iterate backwards through the history and construct the pedigree depth-first.
Expand All @@ -150,10 +150,10 @@ public async Task<Pedigree> GeneratePedigree(string projId, Word word)
var id = word.History[i];
if (!tree.HasAncestor(id))
{
var parent = await _wordRepo.GetWord(projId, id);
var parent = await _wordRepo.GetWord(projectId, id);
if (parent is not null)
{
tree.Parents.Add(await GeneratePedigree(projId, parent));
tree.Parents.Add(await GeneratePedigree(projectId, parent));
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import { useTranslation } from "react-i18next";
import { EditTextDialog } from "components/Dialogs";

interface EntryNoteProps {
buttonId?: string;
noteText: string;
buttonId?: string;
updateNote?: (newText: string) => void | Promise<void>;
}

Expand Down
24 changes: 13 additions & 11 deletions src/components/WordCard/SenseCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,30 +14,32 @@ interface SenseCardProps {
}

export default function SenseCard(props: SenseCardProps): ReactElement {
const gramInfo = props.sense.grammaticalInfo;
const { grammaticalInfo, semanticDomains } = props.sense;

return (
<Card style={{ backgroundColor: "white" }}>
<CardContent style={{ position: "relative", paddingRight: 40 }}>
{/* Icon for part of speech (if any). */}
<Card style={{ backgroundColor: "white", marginBottom: 10 }}>
<CardContent style={{ position: "relative" }}>
{/* Part of speech (if any) */}
<div style={{ position: "absolute", left: 0, top: 0 }}>
{gramInfo.catGroup !== GramCatGroup.Unspecified && (
{grammaticalInfo.catGroup !== GramCatGroup.Unspecified && (
<PartOfSpeechButton
buttonId={`sense-${props.sense.guid}-part-of-speech`}
gramInfo={gramInfo}
gramInfo={grammaticalInfo}
onlyIcon
/>
)}
</div>
{/* List glosses and (if any) definitions. */}

{/* Glosses and (if any) definitions */}
<SenseCardText
hideDefs={props.minimal}
languages={props.languages}
minimal={props.minimal}
sense={props.sense}
/>
{/* List semantic domains. */}
<Grid container spacing={2}>
{props.sense.semanticDomains.map((d) => (

{/* Semantic domains */}
<Grid container spacing={1}>
{semanticDomains.map((d) => (
<Grid item key={`${d.id}_${d.name}`}>
<DomainChip domain={d} provenance={props.provenance} />
</Grid>
Expand Down
51 changes: 25 additions & 26 deletions src/components/WordCard/SenseCardText.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
TableRow,
Typography,
} from "@mui/material";
import { ReactElement } from "react";
import { CSSProperties, ReactElement } from "react";

import { Sense } from "api/models";
import theme from "types/theme";
Expand All @@ -14,27 +14,23 @@ import { TypographyWithFont } from "utilities/fontComponents";
interface SenseInLanguage {
language: string; // bcp-47 code
glossText: string;
definitionText?: string;
definitionText: string;
}

function getSenseInLanguage(
sense: Sense,
language: string,
displaySep = "; "
): SenseInLanguage {
return {
language,
glossText: sense.glosses
.filter((g) => g.language === language)
.map((g) => g.def)
.join(displaySep),
definitionText: sense.definitions.length
? sense.definitions
.filter((d) => d.language === language)
.map((d) => d.text)
.join(displaySep)
: undefined,
};
const glossText = sense.glosses
.filter((g) => g.language === language)
.map((g) => g.def)
.join(displaySep);
const definitionText = sense.definitions
.filter((d) => d.language === language)
.map((d) => d.text)
.join(displaySep);
return { language, glossText, definitionText };
}

function getSenseInLanguages(
Expand All @@ -50,12 +46,12 @@ function getSenseInLanguages(
}

interface SenseCardTextProps {
languages?: string[];
minimal?: boolean;
sense: Sense;
hideDefs?: boolean;
languages?: string[];
}

// Show glosses and (if not minimal) definitions
// Show glosses and (if not hideDefs) definitions.
export default function SenseCardText(props: SenseCardTextProps): ReactElement {
const senseTextInLangs = getSenseInLanguages(props.sense, props.languages);

Expand All @@ -64,7 +60,7 @@ export default function SenseCardText(props: SenseCardTextProps): ReactElement {
<TableBody>
{senseTextInLangs.map((senseInLang, index) => (
<SenseTextRows
hideDefs={props.minimal}
hideDefs={props.hideDefs}
key={index}
senseInLang={senseInLang}
/>
Expand All @@ -74,6 +70,12 @@ export default function SenseCardText(props: SenseCardTextProps): ReactElement {
);
}

const defStyle: CSSProperties = {
borderLeft: "1px solid black",
marginBottom: theme.spacing(1),
paddingLeft: theme.spacing(1),
};

interface SenseTextRowsProps {
senseInLang: SenseInLanguage;
hideDefs?: boolean;
Expand All @@ -83,6 +85,7 @@ function SenseTextRows(props: SenseTextRowsProps): ReactElement {
const lang = props.senseInLang.language;
return (
<>
{/* Gloss */}
<TableRow key={lang}>
<TableCell style={{ borderBottom: "none" }}>
<Typography variant="caption">
Expand All @@ -100,17 +103,13 @@ function SenseTextRows(props: SenseTextRowsProps): ReactElement {
</TypographyWithFont>
</TableCell>
</TableRow>

{/* Definition */}
{!!props.senseInLang.definitionText && !props.hideDefs && (
<TableRow key={lang + "def"}>
<TableCell style={{ borderBottom: "none" }} />
<TableCell style={{ borderBottom: "none" }}>
<div
style={{
borderLeft: "1px solid black",
marginBottom: theme.spacing(1),
paddingLeft: theme.spacing(1),
}}
>
<div style={defStyle}>
<TypographyWithFont color="textSecondary" lang={lang}>
{props.senseInLang.definitionText}
</TypographyWithFont>
Expand Down
24 changes: 15 additions & 9 deletions src/components/WordCard/SummarySenseCard.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Card, CardContent, Chip, Typography } from "@mui/material";
import { Card, CardContent, Chip, Grid, Typography } from "@mui/material";
import { ReactElement } from "react";
import { useTranslation } from "react-i18next";

Expand Down Expand Up @@ -26,9 +26,9 @@ export default function SummarySenseCard(
const domIds = [...new Set(semDoms.map((d) => d.id))].sort();

return (
<Card style={{ backgroundColor: "white" }}>
<CardContent style={{ position: "relative", paddingRight: 40 }}>
{/* Icon for part of speech (if any). */}
<Card style={{ backgroundColor: "white", marginBottom: 10 }}>
<CardContent style={{ position: "relative" }}>
{/* Parts of speech */}
{groupedGramInfo.map((info) => (
<PartOfSpeechButton
buttonId={`senses-${senseGuids}-part-of-speech-${info.catGroup}`}
Expand All @@ -37,14 +37,20 @@ export default function SummarySenseCard(
onlyIcon
/>
))}
{/* Number of senses. */}

{/* Sense count */}
<Typography display="block" variant="h5">
{t("wordHistory.senseCount", { val: props.senses.length })}
</Typography>
{/* Semantic domain numbers. */}
{domIds.map((id) => (
<Chip key={id} label={id} />
))}

{/* Semantic domain numbers */}
<Grid container spacing={1}>
{domIds.map((id) => (
<Grid item key={id}>
<Chip label={id} />
</Grid>
))}
</Grid>
</CardContent>
</Card>
);
Expand Down
40 changes: 21 additions & 19 deletions src/components/WordCard/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
IconButton,
Typography,
} from "@mui/material";
import { ReactElement, useState } from "react";
import { Fragment, ReactElement, useState } from "react";
import { useTranslation } from "react-i18next";

import { Word } from "api/models";
Expand Down Expand Up @@ -35,31 +35,31 @@ export default function WordCard(props: WordCardProps): ReactElement {

return (
<Card style={{ backgroundColor: "lightgray" }}>
<CardContent style={{ position: "relative", paddingRight: 40 }}>
<CardContent style={{ position: "relative" }}>
{/* Vernacular */}
<TypographyWithFont variant="h5" vernacular>
{word.vernacular}
</TypographyWithFont>
{/* Icons for audio, note, flag (if any). */}

{/* Icons for audio, note, flag (if any); button for expand/collapse */}
<div style={{ position: "absolute", right: 0, top: 0 }}>
{!full && <AudioSummary count={audio.length} />}
{!!note.text && <EntryNote noteText={note.text} />}
{flag.active && <FlagButton flag={flag} />}
{full ? (
<IconButtonWithTooltip
buttonId={buttonIdFull(word.id)}
icon={<CloseFullscreen style={{ color: "black" }} />}
onClick={() => setFull(false)}
/>
) : (
<IconButtonWithTooltip
buttonId={buttonIdFull(word.id)}
icon={<OpenInFull style={{ color: "gray" }} />}
onClick={() => setFull(true)}
/>
)}
<IconButtonWithTooltip
buttonId={buttonIdFull(word.id)}
icon={
full ? (
<CloseFullscreen style={{ color: "black" }} />
) : (
<OpenInFull style={{ color: "gray" }} />
)
}
onClick={() => setFull(!full)}
/>
</div>
{/* Audio playback. */}

{/* Audio playback */}
{audio.length > 0 && full && (
<PronunciationsBackend
deleteAudio={() => {}}
Expand All @@ -68,7 +68,8 @@ export default function WordCard(props: WordCardProps): ReactElement {
wordId={id}
/>
)}
{/* Senses. */}

{/* Senses */}
{full ? (
senses.map((s) => (
<SenseCard
Expand All @@ -81,6 +82,7 @@ export default function WordCard(props: WordCardProps): ReactElement {
) : (
<SummarySenseCard senses={senses} />
)}

{/* Timestamps */}
{provenance && (
<Typography display="block" variant="caption">
Expand All @@ -104,6 +106,6 @@ export function AudioSummary(props: { count: number }): ReactElement {
</Badge>
</IconButton>
) : (
<div />
<Fragment />
);
}
2 changes: 1 addition & 1 deletion src/components/WordCard/tests/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ beforeEach(async () => {
});

describe("HistoryCell", () => {
it("has full and summary views", async () => {
it("has summary and full views", async () => {
const button = cardHandle.root.findByProps({ id: buttonId });
expect(cardHandle.root.findByType(AudioSummary).props.count).toEqual(
mockWord.audio.length
Expand Down
Loading

0 comments on commit 63447cf

Please sign in to comment.