Skip to content

Commit

Permalink
Merge branch 'master' into redux-toolkit-project
Browse files Browse the repository at this point in the history
  • Loading branch information
imnasnainaec committed Nov 8, 2023
2 parents de02230 + 2df7dbb commit a981314
Show file tree
Hide file tree
Showing 37 changed files with 834 additions and 764 deletions.
1 change: 1 addition & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@
"thecombine",
"upsert",
"venv",
"verns",
"wordlist",
"wordlists"
],
Expand Down
117 changes: 117 additions & 0 deletions Backend.Tests/Controllers/LiftControllerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,38 @@ private static async Task<string> DownloadAndReadLift(LiftController liftControl
return liftText;
}

[Test]
public void TestUploadLiftFileNoPermission()
{
_liftController.ControllerContext.HttpContext = PermissionServiceMock.UnauthorizedHttpContext();
var result = _liftController.UploadLiftFile(_projId, new FileUpload()).Result;
Assert.That(result, Is.InstanceOf<ForbidResult>());
}

[Test]
public void TestUploadLiftFileInvalidProjectId()
{
var result = _liftController.UploadLiftFile("../hack", new FileUpload()).Result;
Assert.That(result, Is.InstanceOf<UnsupportedMediaTypeResult>());
}

[Test]
public void TestUploadLiftFileAlreadyImported()
{
var projId = _projRepo.Create(new Project { Name = "already has import", LiftImported = true }).Result!.Id;
var result = _liftController.UploadLiftFile(projId, new FileUpload()).Result;
Assert.That(result, Is.InstanceOf<BadRequestObjectResult>());
Assert.That(((BadRequestObjectResult)result).Value, Contains.Substring("LIFT"));
}

[Test]
public void TestUploadLiftFileBadFile()
{
var result = _liftController.UploadLiftFile(_projId, new FileUpload()).Result;
Assert.That(result, Is.InstanceOf<BadRequestObjectResult>());
Assert.That(((BadRequestObjectResult)result).Value, Is.InstanceOf<string>());
}

[Test]
public void TestUploadLiftFileAndGetWritingSystems()
{
Expand Down Expand Up @@ -271,6 +303,21 @@ public void TestFinishUploadLiftFileNothingToFinish()
Assert.That(_liftService.RetrieveImport(UserId), Is.Null);
}

[Test]
public void TestFinishUploadLiftFileNoPermission()
{
_liftController.ControllerContext.HttpContext = PermissionServiceMock.UnauthorizedHttpContext();
var result = _liftController.FinishUploadLiftFile(_projId).Result;
Assert.That(result, Is.InstanceOf<ForbidResult>());
}

[Test]
public void TestFinishUploadLiftFileInvalidProjectId()
{
var result = _liftController.FinishUploadLiftFile("../hack", UserId).Result;
Assert.That(result, Is.InstanceOf<UnsupportedMediaTypeResult>());
}

[Test]
public async Task TestModifiedTimeExportsToLift()
{
Expand All @@ -285,6 +332,35 @@ public async Task TestModifiedTimeExportsToLift()
Assert.That(liftContents, Does.Contain("dateModified=\"2000-01-01T00:00:00Z\""));
}

[Test]
public void TestExportLiftFileNoPermission()
{
_liftController.ControllerContext.HttpContext = PermissionServiceMock.UnauthorizedHttpContext();
var result = _liftController.ExportLiftFile(_projId).Result;
Assert.That(result, Is.InstanceOf<ForbidResult>());
}

[Test]
public void TestExportLiftFileInvalidProjectId()
{
var result = _liftController.ExportLiftFile("../hack").Result;
Assert.That(result, Is.InstanceOf<UnsupportedMediaTypeResult>());
}

[Test]
public void TestExportLiftFileNoProject()
{
var result = _liftController.ExportLiftFile("non-existent-project").Result;
Assert.That(result, Is.InstanceOf<NotFoundObjectResult>());
}

[Test]
public void TestExportLiftFileNoWordsInProject()
{
var result = _liftController.ExportLiftFile(_projId).Result;
Assert.That(result, Is.InstanceOf<BadRequestObjectResult>());
}

[Test]
public void TestExportInvalidProjectId()
{
Expand All @@ -294,6 +370,47 @@ public void TestExportInvalidProjectId()
Throws.TypeOf<MissingProjectException>());
}

[Test]
public void TestDownloadLiftFileNoPermission()
{
_liftController.ControllerContext.HttpContext = PermissionServiceMock.UnauthorizedHttpContext();
var result = _liftController.DownloadLiftFile(_projId).Result;
Assert.That(result, Is.InstanceOf<ForbidResult>());
}

[Test]
public void TestCanUploadLiftNoPermission()
{
_liftController.ControllerContext.HttpContext = PermissionServiceMock.UnauthorizedHttpContext();
var result = _liftController.CanUploadLift(_projId).Result;
Assert.That(result, Is.InstanceOf<ForbidResult>());
}

[Test]
public void TestCanUploadLiftInvalidProjectId()
{
var result = _liftController.CanUploadLift("../hack").Result;
Assert.That(result, Is.InstanceOf<UnsupportedMediaTypeResult>());
}

[Test]
public void TestCanUploadLiftFalse()
{
var projId = _projRepo.Create(new Project { Name = "has import", LiftImported = true }).Result!.Id;
var result = _liftController.CanUploadLift(projId).Result;
Assert.That(result, Is.InstanceOf<OkObjectResult>());
Assert.That(((OkObjectResult)result).Value, Is.False);
}

[Test]
public void TestCanUploadLiftTrue()
{
var projId = _projRepo.Create(new Project { Name = "has no import", LiftImported = false }).Result!.Id;
var result = _liftController.CanUploadLift(projId).Result;
Assert.That(result, Is.InstanceOf<OkObjectResult>());
Assert.That(((OkObjectResult)result).Value, Is.True);
}

/// <summary>
/// Create three words and delete one. Ensure that the deleted word is still exported to Lift format and marked
/// as deleted.
Expand Down
3 changes: 2 additions & 1 deletion Backend.Tests/Mocks/ProjectRepositoryMock.cs
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,8 @@ public Task<ResultOfUpdate> Update(string projectId, Project project)

public Task<bool> CanImportLift(string projectId)
{
return Task.FromResult(true);
var project = _projects.Find(p => p.Id == projectId);
return Task.FromResult(project?.LiftImported != true);
}
}
}
29 changes: 18 additions & 11 deletions Backend/Services/LiftService.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.IO.Compression;
using System.Linq;
Expand Down Expand Up @@ -944,16 +945,17 @@ public void ProcessRangeElement(string range, string id, string guid, string par

// The following are unused and are not implemented, but may still be called by the Lexicon Merger
// They may be useful later if we need to add more complex attributes to words in The Combine
[ExcludeFromCodeCoverage]
public LiftExample GetOrMakeExample(LiftSense sense, Extensible info)
{
return new LiftExample { Content = new LiftMultiText() };
}

[ExcludeFromCodeCoverage]
public LiftObject GetOrMakeParentReversal(LiftObject parent, LiftMultiText contents, string type)
{
return new LiftReversal();
}

[ExcludeFromCodeCoverage]
public LiftSense GetOrMakeSubsense(LiftSense sense, Extensible info, string rawXml)
{
return new LiftSense(info, new Guid(), sense)
Expand All @@ -962,35 +964,40 @@ public LiftSense GetOrMakeSubsense(LiftSense sense, Extensible info, string rawX
Gloss = new LiftMultiText()
};
}

[ExcludeFromCodeCoverage]
public LiftObject MergeInEtymology(LiftEntry entry, string source, string type, LiftMultiText form,
LiftMultiText gloss, string rawXml)
LiftMultiText gloss, string rawXml)
{
return new LiftEtymology();
}

[ExcludeFromCodeCoverage]
public LiftObject MergeInReversal(
LiftSense sense, LiftObject parent, LiftMultiText contents, string type, string rawXml)
LiftSense sense, LiftObject parent, LiftMultiText contents, string type, string rawXml)
{
return new LiftReversal();
}

[ExcludeFromCodeCoverage]
public LiftObject MergeInVariant(LiftEntry entry, LiftMultiText contents, string rawXml)
{
return new LiftVariant();
}

[ExcludeFromCodeCoverage]
public void EntryWasDeleted(Extensible info, DateTime dateDeleted) { }
[ExcludeFromCodeCoverage]
public void MergeInExampleForm(LiftExample example, LiftMultiText multiText) { }

[ExcludeFromCodeCoverage]
public void MergeInPicture(LiftSense sense, string href, LiftMultiText caption) { }
[ExcludeFromCodeCoverage]
public void MergeInRelation(
LiftObject extensible, string relationTypeName, string targetId, string rawXml)
LiftObject extensible, string relationTypeName, string targetId, string rawXml)
{ }
[ExcludeFromCodeCoverage]
public void MergeInSource(LiftExample example, string source) { }
[ExcludeFromCodeCoverage]
public void MergeInTranslationForm(
LiftExample example, string type, LiftMultiText multiText, string rawXml)
LiftExample example, string type, LiftMultiText multiText, string rawXml)
{ }
[ExcludeFromCodeCoverage]
public void ProcessFieldDefinition(string tag, LiftMultiText description) { }
}
}
Expand Down
8 changes: 4 additions & 4 deletions src/components/App/DefaultState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,28 @@ import { defaultState as currentProjectState } from "components/Project/ProjectR
import { defaultState as exportProjectState } from "components/ProjectExport/Redux/ExportProjectReduxTypes";
import { defaultState as pronunciationsState } from "components/Pronunciations/Redux/PronunciationsReduxTypes";
import { defaultState as treeViewState } from "components/TreeView/Redux/TreeViewReduxTypes";
import { defaultState as characterInventoryState } from "goals/CharacterInventory/Redux/CharacterInventoryReducer";
import { defaultState as characterInventoryState } from "goals/CharacterInventory/Redux/CharacterInventoryReduxTypes";
import { defaultState as mergeDuplicateGoal } from "goals/MergeDuplicates/Redux/MergeDupsReducer";
import { defaultState as reviewEntriesState } from "goals/ReviewEntries/ReviewEntriesComponent/Redux/ReviewEntriesReduxTypes";
import { defaultState as analyticsState } from "types/Redux/analyticsReduxTypes";

export const defaultState = {
//login
//login and signup
loginState: { ...loginState },

//project
currentProjectState: { ...currentProjectState },
exportProjectState: { ...exportProjectState },

//data entry and review entries
//data entry and review entries goal
treeViewState: { ...treeViewState },
reviewEntriesState: { ...reviewEntriesState },
pronunciationsState: { ...pronunciationsState },

//goal timeline and current goal
goalsState: { ...goalTimelineState },

//merge duplicates goal
//merge duplicates goal and review deferred duplicates goal
mergeDuplicateGoal: { ...mergeDuplicateGoal },

//character inventory goal
Expand Down
20 changes: 13 additions & 7 deletions src/components/DataEntry/DataEntryTable/NewEntry/SenseDialog.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { Close } from "@mui/icons-material";
import {
Dialog,
DialogContent,
Divider,
Grid,
IconButton,
MenuList,
Typography,
} from "@mui/material";
Expand Down Expand Up @@ -50,7 +51,7 @@ export default function SenseDialog(props: SenseDialogProps): ReactElement {

interface SenseListProps {
selectedWord: Word;
closeDialog: (gloss: string) => void;
closeDialog: (gloss?: string) => void;
analysisLang: string;
}

Expand Down Expand Up @@ -95,11 +96,7 @@ export function SenseList(props: SenseListProps): ReactElement {
);
};

const menuItems: ReactElement[] = [];
for (const s of props.selectedWord.senses) {
menuItems.push(menuItem(s));
menuItems.push(<Divider key={`${s.guid}-divider`} />);
}
const menuItems = props.selectedWord.senses.map(menuItem);
menuItems.push(
<StyledMenuItem key="new-sense" onClick={() => props.closeDialog("")}>
{t("addWords.newSenseFor")}
Expand All @@ -109,7 +106,16 @@ export function SenseList(props: SenseListProps): ReactElement {

return (
<>
{/* Cancel button */}
<IconButton
onClick={() => props.closeDialog()}
style={{ position: "absolute", right: 0, top: 0 }}
>
<Close />
</IconButton>
{/* Header */}
<Typography variant="h3">{t("addWords.selectSense")}</Typography>
{/* Sense options */}
<MenuList autoFocusItem>{menuItems}</MenuList>
</>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import { withStyles } from "@mui/styles";
// Copied from customized menus at https://material-ui.com/components/menus/
const StyledMenuItem = withStyles((theme) => ({
root: {
border: "1px solid gray",
borderRadius: "8px",
marginTop: "8px",
"&:focus": {
backgroundColor: theme.palette.primary.main,
"& .MuiListItemIcon-root, & .MuiListItemText-primary": {
Expand Down
20 changes: 13 additions & 7 deletions src/components/DataEntry/DataEntryTable/NewEntry/VernDialog.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { Close } from "@mui/icons-material";
import {
Dialog,
DialogContent,
Divider,
Grid,
IconButton,
MenuList,
Typography,
} from "@mui/material";
Expand Down Expand Up @@ -50,7 +51,7 @@ export default function VernDialog(props: vernDialogProps): ReactElement {

interface VernListProps {
vernacularWords: Word[];
closeDialog: (wordId: string) => void;
closeDialog: (wordId?: string) => void;
analysisLang?: string;
}

Expand Down Expand Up @@ -96,11 +97,7 @@ export function VernList(props: VernListProps): ReactElement {
);
};

const menuItems: ReactElement[] = [];
for (const w of props.vernacularWords) {
menuItems.push(menuItem(w));
menuItems.push(<Divider key={`${w.id}-divider`} />);
}
const menuItems = props.vernacularWords.map(menuItem);
menuItems.push(
<StyledMenuItem key="new-entry" onClick={() => props.closeDialog("")}>
{t("addWords.newEntryFor")}
Expand All @@ -110,7 +107,16 @@ export function VernList(props: VernListProps): ReactElement {

return (
<>
{/* Cancel button */}
<IconButton
onClick={() => props.closeDialog()}
style={{ position: "absolute", right: 0, top: 0 }}
>
<Close />
</IconButton>
{/* Header */}
<Typography variant="h3">{t("addWords.selectEntry")}</Typography>
{/* Entry options */}
<MenuList autoFocusItem>{menuItems}</MenuList>
</>
);
Expand Down
Loading

0 comments on commit a981314

Please sign in to comment.