diff --git a/client/src/components/author/import-questions/ignored-question-item.tsx b/client/src/components/author/import-questions/ignored-question-item.tsx index 8a5f8f77..82125ef0 100644 --- a/client/src/components/author/import-questions/ignored-question-item.tsx +++ b/client/src/components/author/import-questions/ignored-question-item.tsx @@ -6,7 +6,7 @@ The full terms of this copyright and license should always be found in the root */ import React from "react"; import { Card, CardContent, Typography } from "@mui/material"; -import { IgnoredImportedQuestion } from "../questions-list"; +import { IgnoredImportedQuestion } from "pages/author/subject/importquestions"; export function IgnoredQuestionItem(props: { classes: Record; diff --git a/client/src/components/author/questions-list.tsx b/client/src/components/author/questions-list.tsx index 7889daad..c5d1f7b7 100644 --- a/client/src/components/author/questions-list.tsx +++ b/client/src/components/author/questions-list.tsx @@ -5,7 +5,6 @@ Permission to use, copy, modify, and distribute this software and its documentat The full terms of this copyright and license should always be found in the root directory of this software deliverable as "license.txt" and if these terms are not found with this software, please contact the USC Stevens Center for the full license. */ import React, { useEffect, useState } from "react"; -import { v4 as uuid } from "uuid"; import { DragDropContext, Draggable, @@ -21,17 +20,8 @@ import { TextField, } from "@mui/material"; import AddIcon from "@mui/icons-material/Add"; -import UploadIcon from "@mui/icons-material/Upload"; import { Autocomplete } from "@mui/material"; -import Papa from "papaparse"; -import { - Category, - Question, - QuestionType, - SubjectTypes, - Topic, - UtteranceName, -} from "types"; +import { Category, Question, SubjectTypes, Topic } from "types"; import CategoryListItem from "./category-list-item"; import QuestionListItem from "./question-list-item"; import QuestionEditCard from "./question-edit"; @@ -40,333 +30,22 @@ import { useWithQuestions } from "hooks/graphql/use-with-questions"; import { NewQuestionArgs } from "hooks/graphql/use-with-subject"; import { useWithWindowSize } from "hooks/use-with-window-size"; import { SearchParams } from "hooks/graphql/use-with-data-connection"; -import ImportedQuestionItem from "./import-questions/imported-question-item"; -import IgnoredQuestionItem from "./import-questions/ignored-question-item"; - -function ImportedQuestionsDisplay(props: { - categories: Category[]; - topics: Topic[]; - editQuestion: (val: SubjectQuestionGQL) => void; - removeQuestion: (val: SubjectQuestionGQL) => void; - questionsAdded: SubjectQuestionGQL[]; - questionsIgnored: IgnoredImportedQuestion[]; - classes: Record; -}): JSX.Element { - const { classes, questionsAdded, questionsIgnored, categories, topics } = - props; - return ( - <> - {questionsAdded.length > 0 ? ( - <> - - Successfully Imported Questions - - - {questionsAdded.map((q, i) => ( - - - - ))} - - - ) : undefined} - - {questionsIgnored.length > 0 ? ( - <> - - Un-successful Imports - - - {questionsIgnored.map((q, i) => ( - - - - ))} - - - ) : undefined} - - ); -} - -function ImportQuestionsInstructions(props: { - onQuestionsFileUploaded: (csvFile: File) => void; -}): JSX.Element { - const { onQuestionsFileUploaded } = props; - return ( - <> - Import Questions -
- - Upload a CSV file with the following columns: question, topic, - category, paraphrase1, paraphrase2, ... - -
    -
  • - - The first row of the CSV file should be the column names. - -
  • -
  • - - The question column is required and should be a single question. - -
  • -
  • - - The topic and category columns are required, but you are not - required to enter values into these columns. If you provide a - value, it must exactly match the name of an existing - topic/category, or you will be asked to select from existing - topics and categories. - -
  • -
  • - - There should be a new column for every paraphrase added. - -
  • -
-
- - - - ); -} - -export interface ImportedQuestions { - question: string; - topic: string; - category: string; - paraphrases: string[]; -} - -export interface IgnoredImportedQuestion extends ImportedQuestions { - existingQuestion: SubjectQuestionGQL; -} - -function ImportQuestionsDisplay(props: { - subject: SubjectGQL; - returnAction: () => void; - addQuestions: (qs: SubjectQuestionGQL[]) => void; - editQuestion: (val: SubjectQuestionGQL) => void; - removeQuestion: (val: SubjectQuestionGQL) => void; - classes: Record; - windowHeight: number; -}): JSX.Element { - const { - classes, - returnAction, - addQuestions, - editQuestion, - removeQuestion, - windowHeight, - subject, - } = props; - const [_questionsAdded, setQuestionsAdded] = useState( - [] - ); - const [questionsIgnored, setQuestionsIgnored] = useState< - IgnoredImportedQuestion[] - >([]); - const questionsAdded = subject.questions.filter((q) => - _questionsAdded.find((qa) => qa.question._id === q.question._id) - ); - const categories = subject.categories || []; - const topics = subject.topics || []; - - function addQuestionsToSubject(importedQuestions: ImportedQuestions[]) { - const newQuestionsToAdd: SubjectQuestionGQL[] = []; - const questionsIgnored: IgnoredImportedQuestion[] = []; - - importedQuestions.forEach((importedQuestion) => { - const { - question: importedQuestionText, - topic, - category, - paraphrases: importedParaphrases, - } = importedQuestion; - const questionAlreadyExists = subject.questions.find((subjQ) => { - return ( - subjQ.question.question === importedQuestionText || - subjQ.question.paraphrases.includes(importedQuestionText) || - importedParaphrases.some((p) => { - return ( - subjQ.question.question === p || - subjQ.question.paraphrases.includes(p) - ); - }) - ); - }); - - if (questionAlreadyExists) { - questionsIgnored.push({ - ...importedQuestion, - existingQuestion: questionAlreadyExists, - }); - return; - } - const existingTopic = subject.topics.find((t) => { - return t.name.toLowerCase() === topic.toLowerCase(); - }); - const existingCategory = subject.categories.find((c) => { - return c.name.toLowerCase() === category.toLowerCase(); - }); - - const newQuestion: SubjectQuestionGQL = { - question: { - _id: uuid(), - clientId: uuid(), - question: importedQuestionText, - paraphrases: importedParaphrases, - type: - subject.type === SubjectTypes.UTTERANCES - ? QuestionType.UTTERANCE - : QuestionType.QUESTION, - name: UtteranceName.NONE, - }, - category: existingCategory, - topics: existingTopic ? [existingTopic] : [], - }; - newQuestionsToAdd.push(newQuestion); - }); - setQuestionsAdded(newQuestionsToAdd); - setQuestionsIgnored(questionsIgnored); - addQuestions(newQuestionsToAdd); - } - - function processCsvQuestions(csvContent: string) { - Papa.parse(csvContent, { - complete: (result) => { - const importedQuestions: ImportedQuestions[] = []; - if (result.data.length > 1) { - const rows = result.data.slice(1); // Skip the first row - rows.forEach((row: string[]) => { - const question = row[0]; - const topic = row[1]; - const category = row[2]; - const paraphrases = row.slice(3).filter((p) => p); - if (!question) { - return; - } - importedQuestions.push({ question, topic, category, paraphrases }); - }); - addQuestionsToSubject(importedQuestions); - } - }, - header: false, - }); - } - - function onQuestionsFileUploaded(csvFile: File) { - const reader = new FileReader(); - reader.onload = (event) => { - if (event.target?.result) { - processCsvQuestions(event.target.result as string); - } - }; - reader.readAsText(csvFile); - } - - return ( - <> -
- {questionsAdded.length > 0 || questionsIgnored.length > 0 ? ( - - ) : ( - - )} -
- - - ); -} +import { navigate } from "gatsby"; +import { TwoOptionDialog } from "components/dialog"; export function QuestionsList(props: { - saveCounter: number; + isSubjectEdited: boolean; subject: SubjectGQL; subjectType: SubjectTypes; classes: Record; categories: Category[]; topics: Topic[]; questions: SubjectQuestionGQL[]; + saveSubject: () => Promise; addCategory: () => void; editCategory: (val: Category) => void; removeCategory: (val: Category) => void; addQuestion: (args?: NewQuestionArgs) => void; - addQuestions: (qs: SubjectQuestionGQL[]) => void; editQuestion: (val: SubjectQuestionGQL) => void; removeQuestion: (val: SubjectQuestionGQL) => void; moveQuestion: (toMove: string, moveTo: number, category?: string) => void; @@ -376,16 +55,14 @@ export function QuestionsList(props: { questions, subjectType, subject, - addQuestions, - editQuestion, - saveCounter, + isSubjectEdited, + saveSubject, } = props; const [searchInput, setSearchInput] = useState(); const [selectedQuestion, setSelectedQuestion] = useState(); const [selectedCategory, setSelectedCategory] = useState(); const [allQuestions, setAllQuestions] = useState([]); - const [importQuestionsDisplay, setImportQuestionsDisplay] = - useState(false); + const [showSaveDialog, setShowSaveDialog] = useState(false); const uncategorizedQuestions = questions.filter((q) => !q.category) || []; const { height: windowHeight } = useWithWindowSize(); @@ -398,12 +75,6 @@ export function QuestionsList(props: { }; const { data, nextPage, isLoading } = useWithQuestions(searchParams); - useEffect(() => { - if (saveCounter > 0) { - setImportQuestionsDisplay(false); - } - }, [saveCounter]); - useEffect(() => { if (!data) { return; @@ -438,188 +109,197 @@ export function QuestionsList(props: { return (
- {importQuestionsDisplay ? ( - { - setImportQuestionsDisplay(false); - }} - classes={classes} - windowHeight={windowHeight} - removeQuestion={props.removeQuestion} - /> - ) : ( - <> + + + + + {props.categories.map((category, i) => ( + setSelectedCategory(category.id)} + onBlur={(e) => { + if (e.relatedTarget) { + const element = e.relatedTarget as Element; + if (element.getAttribute("data-cy") !== "add-question") { + setSelectedCategory(undefined); + } + } else { + setSelectedCategory(undefined); + } + }} + > + q.category?.id === category.id + )} + selectedQuestion={selectedQuestion} + selectedCategory={selectedCategory} + removeCategory={props.removeCategory} + updateCategory={props.editCategory} + updateQuestion={props.editQuestion} + removeQuestion={props.removeQuestion} + selectQuestion={selectQuestion} + deselectQuestion={() => selectQuestion(undefined)} + /> + + ))} + + + {(provided) => ( + + {uncategorizedQuestions.map((q, i) => ( + + {(provided) => ( + + selectQuestion(undefined)} + /> + + )} + + ))} + {provided.placeholder} + + )} + + + + {selectedQuestion ? ( - - - - {props.categories.map((category, i) => ( - setSelectedCategory(category.id)} - onBlur={(e) => { - if (e.relatedTarget) { - const element = e.relatedTarget as Element; - if ( - element.getAttribute("data-cy") !== "add-question" - ) { - setSelectedCategory(undefined); - } - } else { - setSelectedCategory(undefined); - } - }} - > - q.category?.id === category.id - )} - selectedQuestion={selectedQuestion} - selectedCategory={selectedCategory} - removeCategory={props.removeCategory} - updateCategory={props.editCategory} - updateQuestion={props.editQuestion} - removeQuestion={props.removeQuestion} - selectQuestion={selectQuestion} - deselectQuestion={() => selectQuestion(undefined)} - /> - - ))} - - - {(provided) => ( - - {uncategorizedQuestions.map((q, i) => ( - - {(provided) => ( - - - selectQuestion(undefined) - } - /> - - )} - - ))} - {provided.placeholder} - - )} - - - - {selectedQuestion ? ( - - q.question._id === selectedQuestion - )} - updateQuestion={props.editQuestion} - onDeselect={() => selectQuestion(undefined)} - /> - - ) : undefined} + q.question._id === selectedQuestion + )} + updateQuestion={props.editQuestion} + onDeselect={() => selectQuestion(undefined)} + /> - option.question} - onChange={(e, v) => { - if (v) { - setSearchInput(v); + ) : undefined} + + option.question} + onChange={(e, v) => { + if (v) { + setSearchInput(v); + } + }} + style={{ minWidth: 300, flexGrow: 1, marginTop: 10 }} + renderOption={(props, option) => ( + + {option.question} + + )} + renderInput={(params) => + isLoading ? ( + + ) : ( + + ) + } + /> + + + + { + setShowSaveDialog(false); + }, + }} + option2={{ + display: "Save and Continue", + onClick: () => { + setShowSaveDialog(false); + saveSubject().then((s) => { + if (s) { + navigate(`/author/subject/importquestions/?id=${s._id}`); } - }} - style={{ minWidth: 300, flexGrow: 1, marginTop: 10 }} - renderOption={(props, option) => ( - - {option.question} - - )} - renderInput={(params) => - isLoading ? ( - - ) : ( - - ) - } - /> - - - - - )} + }); + }, + }} + />
); } diff --git a/client/src/hooks/graphql/use-with-subject.tsx b/client/src/hooks/graphql/use-with-subject.tsx index ef9bdc5b..721573ee 100644 --- a/client/src/hooks/graphql/use-with-subject.tsx +++ b/client/src/hooks/graphql/use-with-subject.tsx @@ -41,7 +41,7 @@ interface UseWithSubject { error: LoadingError | undefined; userCanArchiveSubjects: boolean; editData: (d: Partial) => void; - saveSubject: () => void; + saveSubject: (subj?: SubjectGQL) => Promise; addCategory: () => void; updateCategory: (val: Category) => void; removeCategory: (val: Category) => void; @@ -93,19 +93,25 @@ export function useWithSubject( return fetchSubject(subjectId); } - function saveSubject() { - saveAndReturnData({ - action: async (editedData: SubjectGQL) => { - const updated = await updateSubject(editedData, accessToken); - // we need to reload the mentor after updating a subject because - // the subjects and questions and answers might have changed - // would be better to edit in place but for now do the easy (but more expensive) way and - // change this later if needed - // this doesn't happen very often anyway - loadMentor(); - return updated; + async function saveSubject( + subject?: SubjectGQL + ): Promise { + const newSubj = await saveAndReturnData( + { + action: async (editedData: SubjectGQL) => { + const updated = await updateSubject(editedData, accessToken); + // we need to reload the mentor after updating a subject because + // the subjects and questions and answers might have changed + // would be better to edit in place but for now do the easy (but more expensive) way and + // change this later if needed + // this doesn't happen very often anyway + loadMentor(); + return updated; + }, }, - }); + subject + ); + return newSubj; } function addCategory() { diff --git a/client/src/pages/author/subject.tsx b/client/src/pages/author/subject.tsx index 06910062..b7cff0d8 100644 --- a/client/src/pages/author/subject.tsx +++ b/client/src/pages/author/subject.tsx @@ -62,7 +62,6 @@ function SubjectPage(props: { }): JSX.Element { const { classes } = useStyles(); const [tab, setTab] = useState("1"); - const [saveCounter, setSaveCounter] = useState(0); const [deleteSubjectDialog, setDeleteSubjectDialog] = useState(false); const [deleteInProgress, setDeleteInProgress] = useState(false); @@ -85,7 +84,6 @@ function SubjectPage(props: { removeTopic, moveTopic, addQuestion, - addQuestions, updateQuestion, removeQuestion, moveQuestion, @@ -204,7 +202,8 @@ function SubjectPage(props: { { saveSubject(); - setSaveCounter((prevValue) => prevValue + 1); }} > {subjectExists ? "Save" : "Create"} diff --git a/client/src/pages/author/subject/importquestions.tsx b/client/src/pages/author/subject/importquestions.tsx new file mode 100644 index 00000000..0dfeda44 --- /dev/null +++ b/client/src/pages/author/subject/importquestions.tsx @@ -0,0 +1,479 @@ +/* +This software is Copyright ©️ 2020 The University of Southern California. All Rights Reserved. +Permission to use, copy, modify, and distribute this software and its documentation for educational, research and non-profit purposes, without fee, and without a written agreement is hereby granted, provided that the above copyright notice and subject to the full license file found in the root of this software deliverable. Permission to make commercial use of this software may be obtained by contacting: USC Stevens Center for Innovation University of Southern California 1150 S. Olive Street, Suite 2300, Los Angeles, CA 90115, USA Email: accounting@stevens.usc.edu + +The full terms of this copyright and license should always be found in the root directory of this software deliverable as "license.txt" and if these terms are not found with this software, please contact the USC Stevens Center for the full license. +*/ +import React, { useState } from "react"; +import { + Button, + CircularProgress, + List, + ListItem, + Theme, + Typography, +} from "@mui/material"; +import { makeStyles } from "tss-react/mui"; + +import NavBar from "components/nav-bar"; +import { v4 as uuid } from "uuid"; +import UploadIcon from "@mui/icons-material/Upload"; +import withLocation from "wrap-with-location"; +import withAuthorizationOnly from "hooks/wrap-with-authorization-only"; +import { useActiveMentor } from "store/slices/mentor/useActiveMentor"; +import { useWithSubject } from "hooks/graphql/use-with-subject"; +import { ErrorDialog, LoadingDialog, TwoOptionDialog } from "components/dialog"; +import { useWithWindowSize } from "hooks/use-with-window-size"; +import { + Category, + QuestionType, + SubjectTypes, + Topic, + UtteranceName, +} from "types"; +import { navigate } from "gatsby"; +import { SubjectGQL, SubjectQuestionGQL } from "types-gql"; +import ImportedQuestionItem from "components/author/import-questions/imported-question-item"; +import IgnoredQuestionItem from "components/author/import-questions/ignored-question-item"; +import Papa from "papaparse"; + +const useStyles = makeStyles({ name: { ImportQuestionsPage } })( + (theme: Theme) => ({ + root: { + height: "100%", + display: "flex", + flexDirection: "column", + alignItems: "center", + margin: 0, + }, + row: { + display: "flex", + flexDirection: "row", + alignItems: "center", + }, + tab: { + width: "95%", + }, + button: { + width: 200, + margin: theme.spacing(2), + }, + list: { + background: "#F5F5F5", + }, + }) +); + +function ImportedQuestionsDisplay(props: { + categories: Category[]; + topics: Topic[]; + editQuestion: (val: SubjectQuestionGQL) => void; + removeQuestion: (val: SubjectQuestionGQL) => void; + questionsAdded: SubjectQuestionGQL[]; + questionsIgnored: IgnoredImportedQuestion[]; + classes: Record; +}): JSX.Element { + const { classes, questionsAdded, questionsIgnored, categories, topics } = + props; + return ( + <> + {questionsAdded.length > 0 ? ( + <> + + Successfully Imported Questions + + + {questionsAdded.map((q, i) => ( + + + + ))} + + + ) : undefined} + + {questionsIgnored.length > 0 ? ( + <> + + Un-successful Imports + + + {questionsIgnored.map((q, i) => ( + + + + ))} + + + ) : undefined} + + ); +} + +function ImportQuestionsInstructions(props: { + onQuestionsFileUploaded: (csvFile: File) => void; +}): JSX.Element { + const { onQuestionsFileUploaded } = props; + return ( + <> +
+ + Upload a CSV file with the following columns: question, topic, + category, paraphrase1, paraphrase2, ... + +
    +
  • + + The first row of the CSV file should be the column names. + +
  • +
  • + + The question column is required and should be a single question. + +
  • +
  • + + The topic and category columns are required, but you are not + required to enter values into these columns. If you provide a + value, it must exactly match the name of an existing + topic/category, or you will be asked to select from existing + topics and categories. + +
  • +
  • + + There should be a new column for every paraphrase added. + +
  • +
+
+ + + + ); +} + +export interface ImportedQuestions { + question: string; + topic: string; + category: string; + paraphrases: string[]; +} + +export interface IgnoredImportedQuestion extends ImportedQuestions { + existingQuestion: SubjectQuestionGQL; +} + +function ImportQuestionsDisplay(props: { + subject: SubjectGQL; + addQuestions: (qs: SubjectQuestionGQL[]) => void; + editQuestion: (val: SubjectQuestionGQL) => void; + removeQuestion: (val: SubjectQuestionGQL) => void; + classes: Record; + windowHeight: number; +}): JSX.Element { + const { + classes, + addQuestions, + editQuestion, + removeQuestion, + windowHeight, + subject, + } = props; + const [_questionsAdded, setQuestionsAdded] = useState( + [] + ); + const [questionsIgnored, setQuestionsIgnored] = useState< + IgnoredImportedQuestion[] + >([]); + const questionsAdded = subject.questions.filter((q) => + _questionsAdded.find((qa) => qa.question._id === q.question._id) + ); + const categories = subject.categories || []; + const topics = subject.topics || []; + + function addQuestionsToSubject(importedQuestions: ImportedQuestions[]) { + const newQuestionsToAdd: SubjectQuestionGQL[] = []; + const questionsIgnored: IgnoredImportedQuestion[] = []; + + importedQuestions.forEach((importedQuestion) => { + const { + question: importedQuestionText, + topic, + category, + paraphrases: importedParaphrases, + } = importedQuestion; + const questionAlreadyExists = subject.questions.find((subjQ) => { + return ( + subjQ.question.question === importedQuestionText || + subjQ.question.paraphrases.includes(importedQuestionText) || + importedParaphrases.some((p) => { + return ( + subjQ.question.question === p || + subjQ.question.paraphrases.includes(p) + ); + }) + ); + }); + + if (questionAlreadyExists) { + questionsIgnored.push({ + ...importedQuestion, + existingQuestion: questionAlreadyExists, + }); + return; + } + const existingTopic = subject.topics.find((t) => { + return t.name.toLowerCase() === topic.toLowerCase(); + }); + const existingCategory = subject.categories.find((c) => { + return c.name.toLowerCase() === category.toLowerCase(); + }); + + const newQuestion: SubjectQuestionGQL = { + question: { + _id: uuid(), + clientId: uuid(), + question: importedQuestionText, + paraphrases: importedParaphrases, + type: + subject.type === SubjectTypes.UTTERANCES + ? QuestionType.UTTERANCE + : QuestionType.QUESTION, + name: UtteranceName.NONE, + }, + category: existingCategory, + topics: existingTopic ? [existingTopic] : [], + }; + newQuestionsToAdd.push(newQuestion); + }); + setQuestionsAdded(newQuestionsToAdd); + setQuestionsIgnored(questionsIgnored); + addQuestions(newQuestionsToAdd); + } + + function processCsvQuestions(csvContent: string) { + Papa.parse(csvContent, { + complete: (result) => { + const importedQuestions: ImportedQuestions[] = []; + if (result.data.length > 1) { + const rows = result.data.slice(1); // Skip the first row + rows.forEach((row: string[]) => { + const question = row[0]; + const topic = row[1]; + const category = row[2]; + const paraphrases = row.slice(3).filter((p) => p); + if (!question) { + return; + } + importedQuestions.push({ question, topic, category, paraphrases }); + }); + addQuestionsToSubject(importedQuestions); + } + }, + header: false, + }); + } + + function onQuestionsFileUploaded(csvFile: File) { + const reader = new FileReader(); + reader.onload = (event) => { + if (event.target?.result) { + processCsvQuestions(event.target.result as string); + } + }; + reader.readAsText(csvFile); + } + + return ( + <> +
+ {questionsAdded.length > 0 || questionsIgnored.length > 0 ? ( + + ) : ( + + )} +
+ + ); +} + +export function ImportQuestionsPage(props: { + accessToken: string; + search: { id?: string }; +}): JSX.Element { + const { classes } = useStyles(); + const { getData, isLoading: isMentorLoading } = useActiveMentor(); + const mentorId = getData((state) => state.data?._id); + const { + editedData: editedSubject, + error: subjectError, + isEdited: isSubjectEdited, + isLoading: isSubjectLoading, + isSaving: isSubjectSaving, + saveSubject, + addQuestions, + updateQuestion, + removeQuestion, + } = useWithSubject(props.search.id || "", props.accessToken); + const { height: windowHeight } = useWithWindowSize(); + const [cancelImportDialogue, setCancelImportDialogue] = + useState(false); + + if (!mentorId || !editedSubject) { + return ( +
+ + +
+ ); + } + + return ( +
+ + + + setCancelImportDialogue(false), + }} + option2={{ + display: "Yes", + onClick: () => { + setCancelImportDialogue(false); + navigate(`/author/subject/?id=${editedSubject._id}`); + }, + }} + /> + +
+ + +
+ + setCancelImportDialogue(false), + }} + option2={{ + display: "Yes", + onClick: () => { + setCancelImportDialogue(false); + navigate(`/author/subject/?id=${editedSubject._id}`); + }, + }} + /> + +
+ ); +} + +export default withAuthorizationOnly(withLocation(ImportQuestionsPage)); diff --git a/cypress/cypress/integration/import-questions.spec.ts b/cypress/cypress/integration/import-questions.spec.ts new file mode 100644 index 00000000..6a43f423 --- /dev/null +++ b/cypress/cypress/integration/import-questions.spec.ts @@ -0,0 +1,21 @@ +/* +This software is Copyright ©️ 2020 The University of Southern California. All Rights Reserved. +Permission to use, copy, modify, and distribute this software and its documentation for educational, research and non-profit purposes, without fee, and without a written agreement is hereby granted, provided that the above copyright notice and subject to the full license file found in the root of this software deliverable. Permission to make commercial use of this software may be obtained by contacting: USC Stevens Center for Innovation University of Southern California 1150 S. Olive Street, Suite 2300, Los Angeles, CA 90115, USA Email: accounting@stevens.usc.edu + +The full terms of this copyright and license should always be found in the root directory of this software deliverable as "license.txt" and if these terms are not found with this software, please contact the USC Stevens Center for the full license. +*/ +import { + cySetup, + cyMockDefault, + mockGQL, + testIndexedDbData, +} from "../support/functions"; +import allSubjects from "../fixtures/subjects/all-subjects"; + +describe("Import Questions", () => { + it("Questions in queue are excluded in trending feedback request", () => { + cySetup(cy); + cyMockDefault(cy); + cy.visit("/author/subject/importquestions/?id=leadership"); + }); +});