Buttons to restart test or use incorrect answers
This commit is contained in:
@@ -297,6 +297,95 @@ exports.createProgress = functions.https.onCall((data, context) => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates new progress document using the incorrect answers from another progess document.
|
||||||
|
* @param {string} data The progress ID of the existing progress document to use.
|
||||||
|
* @return {string} The ID of the created progress document.
|
||||||
|
*/
|
||||||
|
exports.createProgressWithIncorrect = functions.https.onCall((data, context) => {
|
||||||
|
const uid = LOCAL_TESTING ? "M3JPrFRH6Fdo8XMUbF0l2zVZUCH3" : context.auth.uid;
|
||||||
|
|
||||||
|
if (context.app == undefined && !LOCAL_TESTING) {
|
||||||
|
throw new functions.https.HttpsError(
|
||||||
|
"failed-precondition",
|
||||||
|
"The function must be called from an App Check verified app.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return db.runTransaction(async (transaction) => {
|
||||||
|
if (typeof data !== "string") {
|
||||||
|
throw new functions.https.HttpsError("invalid-argument", "Progress ID must be a string");
|
||||||
|
}
|
||||||
|
|
||||||
|
const oldProgressDocId = db.collection("progress").doc(data);
|
||||||
|
return transaction.get(oldProgressDocId)
|
||||||
|
.then(async (doc) => {
|
||||||
|
if (!doc.exists) throw new functions.https.HttpsError("invalid-argument", "Progress record doesn't exist");
|
||||||
|
if (doc.data().uid !== uid) throw new functions.https.HttpsError("permission-denied", "Can't use other users' progress records");
|
||||||
|
if (doc.data().incorrect.length < 1) throw new functions.https.HttpsError("failed-precondition", "Progress record must have at least one incorrect answer");
|
||||||
|
|
||||||
|
let progressData = doc.data();
|
||||||
|
let dataToSet = {
|
||||||
|
correct: [],
|
||||||
|
incorrect: [],
|
||||||
|
questions: shuffleArray([... new Set(progressData.incorrect)]),
|
||||||
|
duration: null,
|
||||||
|
progress: 0,
|
||||||
|
start_time: Date.now(),
|
||||||
|
set_title: progressData.set_title,
|
||||||
|
uid: progressData.uid,
|
||||||
|
switch_language: progressData.switch_language,
|
||||||
|
mode: progressData.mode,
|
||||||
|
current_correct: [],
|
||||||
|
typo: false,
|
||||||
|
setIds: progressData.setIds,
|
||||||
|
};
|
||||||
|
if (progressData.mode === "lives") {
|
||||||
|
dataToSet.lives = progressData.start_lives;
|
||||||
|
dataToSet.start_lives = progressData.start_lives;
|
||||||
|
}
|
||||||
|
|
||||||
|
const newProgressDocId = db.collection("progress").doc();
|
||||||
|
let batches = [db.batch()];
|
||||||
|
let promises = [];
|
||||||
|
|
||||||
|
dataToSet.questions.map(async (vocabId, index) => {
|
||||||
|
if (index % 248 === 0) {
|
||||||
|
batches.push(db.batch());
|
||||||
|
}
|
||||||
|
let currentBatchIndex = batches.length - 1;
|
||||||
|
promises.push(transaction.get(oldProgressDocId.collection("terms").doc(vocabId))
|
||||||
|
.then((termDoc) => {
|
||||||
|
return batches[currentBatchIndex].set(
|
||||||
|
newProgressDocId.collection("terms").doc(vocabId),
|
||||||
|
termDoc.data()
|
||||||
|
);
|
||||||
|
}));
|
||||||
|
promises.push(transaction.get(oldProgressDocId.collection("definitions").doc(vocabId))
|
||||||
|
.then((termDoc) => {
|
||||||
|
return batches[currentBatchIndex].set(
|
||||||
|
newProgressDocId.collection("definitions").doc(vocabId),
|
||||||
|
termDoc.data()
|
||||||
|
);
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
batches[batches.length - 1].set(
|
||||||
|
newProgressDocId,
|
||||||
|
dataToSet
|
||||||
|
);
|
||||||
|
|
||||||
|
await Promise.all(promises);
|
||||||
|
|
||||||
|
await Promise.all(batches.map((batch) => batch.commit()));
|
||||||
|
|
||||||
|
return newProgressDocId.id;
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
throw new functions.https.HttpsError("unknown", "Can't create new progress record from existing one");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Processes a response to a question in a vocab set.
|
* Processes a response to a question in a vocab set.
|
||||||
* @param {string} progressId The ID of the progress file to retrieve the prompt from.
|
* @param {string} progressId The ID of the progress file to retrieve the prompt from.
|
||||||
|
|||||||
391
src/Progress.js
391
src/Progress.js
@@ -3,18 +3,60 @@ import { withRouter } from "react-router-dom";
|
|||||||
import { HomeRounded as HomeRoundedIcon, ArrowForwardRounded as ArrowForwardRoundedIcon, SettingsRounded as SettingsRoundedIcon, CloseRounded as CloseRoundedIcon, PeopleRounded as PeopleRoundedIcon, QuestionAnswerRounded as QuestionAnswerRoundedIcon } from "@material-ui/icons";
|
import { HomeRounded as HomeRoundedIcon, ArrowForwardRounded as ArrowForwardRoundedIcon, SettingsRounded as SettingsRoundedIcon, CloseRounded as CloseRoundedIcon, PeopleRounded as PeopleRoundedIcon, QuestionAnswerRounded as QuestionAnswerRoundedIcon } from "@material-ui/icons";
|
||||||
import NavBar from "./NavBar";
|
import NavBar from "./NavBar";
|
||||||
import Button from "./Button";
|
import Button from "./Button";
|
||||||
import LinkButton from "./LinkButton";
|
|
||||||
import Error404 from "./Error404";
|
import Error404 from "./Error404";
|
||||||
import SettingsContent from "./SettingsContent";
|
import SettingsContent from "./SettingsContent";
|
||||||
import Footer from "./Footer";
|
import Footer from "./Footer";
|
||||||
import LineChart from './LineChart';
|
import LineChart from './LineChart';
|
||||||
import ProgressStats from './ProgressStats';
|
import ProgressStats from './ProgressStats';
|
||||||
|
import ConfirmationDialog from "./ConfirmationDialog";
|
||||||
|
|
||||||
import "./css/PopUp.css";
|
import "./css/PopUp.css";
|
||||||
import "./css/Progress.css";
|
import "./css/Progress.css";
|
||||||
import "./css/Chart.css";
|
import "./css/Chart.css";
|
||||||
|
|
||||||
export default withRouter(class Progress extends React.Component {
|
export default withRouter(class Progress extends React.Component {
|
||||||
|
changeableStateItems = {
|
||||||
|
loading: false,
|
||||||
|
canProceed: true,
|
||||||
|
canStartTest: true,
|
||||||
|
showTestRestart: false,
|
||||||
|
showIncorrectTestStart: false,
|
||||||
|
progressInaccessible: false,
|
||||||
|
correct: 0,
|
||||||
|
incorrect: 0,
|
||||||
|
totalQuestions: 0,
|
||||||
|
progress: 0,
|
||||||
|
setTitle: "",
|
||||||
|
switchLanguage: false,
|
||||||
|
answerInput: "",
|
||||||
|
currentPrompt: "",
|
||||||
|
currentSound: false,
|
||||||
|
currentSetOwner: "",
|
||||||
|
nextPrompt: "",
|
||||||
|
nextSound: false,
|
||||||
|
nextSetOwner: "",
|
||||||
|
currentAnswerStatus: null,
|
||||||
|
currentCorrect: [],
|
||||||
|
moreAnswers: true,
|
||||||
|
duration: 0,
|
||||||
|
incorrectAnswers: {},
|
||||||
|
showSettings: false,
|
||||||
|
soundInput: this.props.sound,
|
||||||
|
themeInput: this.props.theme,
|
||||||
|
coloredEdgesInput: this.props.coloredEdges,
|
||||||
|
setIds: [],
|
||||||
|
attemptNumber: 1,
|
||||||
|
attemptHistory: {},
|
||||||
|
questions: [],
|
||||||
|
originalTotalQuestions: 1,
|
||||||
|
lives: 1,
|
||||||
|
startLives: null,
|
||||||
|
setComplete: false,
|
||||||
|
averagePercentage: null,
|
||||||
|
pageLoaded: false,
|
||||||
|
startTime: null,
|
||||||
|
}
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
@@ -22,9 +64,9 @@ export default withRouter(class Progress extends React.Component {
|
|||||||
db: props.db,
|
db: props.db,
|
||||||
functions: {
|
functions: {
|
||||||
processAnswer: props.functions.httpsCallable("processAnswer"),
|
processAnswer: props.functions.httpsCallable("processAnswer"),
|
||||||
|
createProgress: props.functions.httpsCallable("createProgress"),
|
||||||
|
createProgressWithIncorrect: props.functions.httpsCallable("createProgressWithIncorrect"),
|
||||||
},
|
},
|
||||||
loading: false,
|
|
||||||
canProceed: true,
|
|
||||||
navbarItems: [
|
navbarItems: [
|
||||||
{
|
{
|
||||||
type: "button",
|
type: "button",
|
||||||
@@ -39,40 +81,7 @@ export default withRouter(class Progress extends React.Component {
|
|||||||
hideTextMobile: true,
|
hideTextMobile: true,
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
progressInaccessible: false,
|
...this.changeableStateItems,
|
||||||
correct: 0,
|
|
||||||
incorrect: 0,
|
|
||||||
totalQuestions: 0,
|
|
||||||
progress: 0,
|
|
||||||
setTitle: "",
|
|
||||||
switchLanguage: false,
|
|
||||||
answerInput: "",
|
|
||||||
currentPrompt: "",
|
|
||||||
currentSound: false,
|
|
||||||
currentSetOwner: "",
|
|
||||||
nextPrompt: "",
|
|
||||||
nextSound: false,
|
|
||||||
nextSetOwner: "",
|
|
||||||
currentAnswerStatus: null,
|
|
||||||
currentCorrect: [],
|
|
||||||
moreAnswers: true,
|
|
||||||
duration: 0,
|
|
||||||
incorrectAnswers: {},
|
|
||||||
showSettings: false,
|
|
||||||
soundInput: this.props.sound,
|
|
||||||
themeInput: this.props.theme,
|
|
||||||
coloredEdgesInput: this.props.coloredEdges,
|
|
||||||
setIds: [],
|
|
||||||
attemptNumber: 1,
|
|
||||||
attemptHistory: {},
|
|
||||||
questions: [],
|
|
||||||
originalTotalQuestions: 1,
|
|
||||||
lives: 1,
|
|
||||||
startLives: null,
|
|
||||||
setComplete: false,
|
|
||||||
averagePercentage: null,
|
|
||||||
pageLoaded: false,
|
|
||||||
startTime: null,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let isMounted = true;
|
let isMounted = true;
|
||||||
@@ -87,6 +96,10 @@ export default withRouter(class Progress extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async componentDidMount() {
|
async componentDidMount() {
|
||||||
|
this.unlisten = this.props.history.listen((location, action) => {
|
||||||
|
if (location.pathname.startsWith("/progress/")) this.setState(this.changeableStateItems, () => this.componentDidMount());
|
||||||
|
});
|
||||||
|
|
||||||
const progressId = this.props.match.params.progressId;
|
const progressId = this.props.match.params.progressId;
|
||||||
const progressRef = this.state.db.collection("progress").doc(progressId);
|
const progressRef = this.state.db.collection("progress").doc(progressId);
|
||||||
|
|
||||||
@@ -129,94 +142,95 @@ export default withRouter(class Progress extends React.Component {
|
|||||||
];
|
];
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!newState.progressInaccessible && !setDone) {
|
if (!newState.progressInaccessible) {
|
||||||
let nextPromptRef;
|
if (!setDone) {
|
||||||
if (!newState.switchLanguage) {
|
let nextPromptRef;
|
||||||
nextPromptRef = progressRef
|
if (!newState.switchLanguage) {
|
||||||
.collection("terms")
|
nextPromptRef = progressRef
|
||||||
.doc(newState.questions[newState.progress]);
|
.collection("terms")
|
||||||
} else {
|
.doc(newState.questions[newState.progress]);
|
||||||
nextPromptRef = progressRef
|
} else {
|
||||||
.collection("definitions")
|
nextPromptRef = progressRef
|
||||||
.doc(newState.questions[newState.progress]);
|
.collection("definitions")
|
||||||
}
|
.doc(newState.questions[newState.progress]);
|
||||||
|
}
|
||||||
await nextPromptRef.get().then((doc) => {
|
|
||||||
newState.currentPrompt = doc.data().item;
|
await nextPromptRef.get().then((doc) => {
|
||||||
newState.currentSound = doc.data().sound === true;
|
newState.currentPrompt = doc.data().item;
|
||||||
}).catch((error) => {
|
newState.currentSound = doc.data().sound === true;
|
||||||
newState.progressInaccessible = true;
|
|
||||||
console.log(`Progress data inaccessible: ${error}`);
|
|
||||||
});
|
|
||||||
} else if (setDone) {
|
|
||||||
newState.moreAnswers = false;
|
|
||||||
newState.currentAnswerStatus = true;
|
|
||||||
newState.duration = duration;
|
|
||||||
|
|
||||||
let promises = [];
|
|
||||||
|
|
||||||
promises.push(this.state.db.collection("progress")
|
|
||||||
.where("uid", "==", this.state.user.uid)
|
|
||||||
.where("setIds", "==", newState.setIds)
|
|
||||||
.orderBy("start_time")
|
|
||||||
.get()
|
|
||||||
.then((querySnapshot) => {
|
|
||||||
newState.attemptNumber = querySnapshot.docs.map((doc) => doc.id).indexOf(this.props.match.params.progressId) + 1;
|
|
||||||
if (querySnapshot.docs.length > 1)
|
|
||||||
newState.attemptHistory = querySnapshot.docs.filter((doc) => doc.data().duration !== null)
|
|
||||||
.map((doc) => {
|
|
||||||
if (doc.id === this.props.match.params.progressId) newState.startTime = doc.data().start_time;
|
|
||||||
return {
|
|
||||||
x: new Date(doc.data().start_time),
|
|
||||||
y: (doc.data().correct.length / doc.data().questions.length * 100),
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
promises.push(this.state.db.collection("completed_progress")
|
|
||||||
.doc(setIds.sort().join("__"))
|
|
||||||
.get()
|
|
||||||
.then((completedProgressDoc) => {
|
|
||||||
newState.averagePercentage = (completedProgressDoc.data().total_percentage / completedProgressDoc.data().attempts).toFixed(2);
|
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
console.log(`Couldn't get average percentage: ${error}`);
|
newState.progressInaccessible = true;
|
||||||
newState.averagePercentage = null;
|
console.log(`Progress data inaccessible: ${error}`);
|
||||||
}));
|
});
|
||||||
|
} else {
|
||||||
|
newState.moreAnswers = false;
|
||||||
|
newState.currentAnswerStatus = true;
|
||||||
|
newState.duration = duration;
|
||||||
|
|
||||||
if (incorrectAnswers.length > 0) {
|
let promises = [];
|
||||||
newState.incorrectAnswers = {};
|
promises.push(this.state.db.collection("progress")
|
||||||
|
.where("uid", "==", this.state.user.uid)
|
||||||
promises.push(Promise.all(incorrectAnswers.map((vocabId) => {
|
.where("setIds", "==", newState.setIds)
|
||||||
if (newState.incorrectAnswers[vocabId]) {
|
.orderBy("start_time")
|
||||||
return newState.incorrectAnswers[vocabId].count++;
|
.get()
|
||||||
} else {
|
.then((querySnapshot) => {
|
||||||
newState.incorrectAnswers[vocabId] = {
|
newState.attemptNumber = querySnapshot.docs.map((doc) => doc.id).indexOf(this.props.match.params.progressId) + 1;
|
||||||
count: 1,
|
if (querySnapshot.docs.length > 1)
|
||||||
};
|
newState.attemptHistory = querySnapshot.docs.filter((doc) => doc.data().duration !== null)
|
||||||
|
.map((doc) => {
|
||||||
|
if (doc.id === this.props.match.params.progressId) newState.startTime = doc.data().start_time;
|
||||||
|
return {
|
||||||
|
x: new Date(doc.data().start_time),
|
||||||
|
y: (doc.data().correct.length / doc.data().questions.length * 100),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
return Promise.all([
|
promises.push(this.state.db.collection("completed_progress")
|
||||||
progressRef.collection("terms")
|
.doc(setIds.sort().join("__"))
|
||||||
.doc(vocabId)
|
.get()
|
||||||
.get().then((termDoc) => {
|
.then((completedProgressDoc) => {
|
||||||
newState.switchLanguage ? newState.incorrectAnswers[vocabId].answer = termDoc.data().item.split("/") : newState.incorrectAnswers[vocabId].prompt = termDoc.data().item;
|
newState.averagePercentage = (completedProgressDoc.data().total_percentage / completedProgressDoc.data().attempts).toFixed(2);
|
||||||
}),
|
}).catch((error) => {
|
||||||
progressRef.collection("definitions")
|
console.log(`Couldn't get average percentage: ${error}`);
|
||||||
.doc(vocabId)
|
newState.averagePercentage = null;
|
||||||
.get().then((definitionDoc) => {
|
}));
|
||||||
newState.switchLanguage ? newState.incorrectAnswers[vocabId].prompt = definitionDoc.data().item : newState.incorrectAnswers[vocabId].answer = definitionDoc.data().item.split("/");
|
|
||||||
})
|
if (incorrectAnswers.length > 0) {
|
||||||
]);
|
newState.incorrectAnswers = {};
|
||||||
}
|
|
||||||
})).catch((error) => {
|
promises.push(Promise.all(incorrectAnswers.map((vocabId) => {
|
||||||
console.log(`Couldn't retrieve incorrect answers: ${error}`);
|
if (newState.incorrectAnswers[vocabId]) {
|
||||||
}));
|
return newState.incorrectAnswers[vocabId].count++;
|
||||||
|
} else {
|
||||||
|
newState.incorrectAnswers[vocabId] = {
|
||||||
|
count: 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
return Promise.all([
|
||||||
|
progressRef.collection("terms")
|
||||||
|
.doc(vocabId)
|
||||||
|
.get().then((termDoc) => {
|
||||||
|
newState.switchLanguage ? newState.incorrectAnswers[vocabId].answer = termDoc.data().item.split("/") : newState.incorrectAnswers[vocabId].prompt = termDoc.data().item;
|
||||||
|
}),
|
||||||
|
progressRef.collection("definitions")
|
||||||
|
.doc(vocabId)
|
||||||
|
.get().then((definitionDoc) => {
|
||||||
|
newState.switchLanguage ? newState.incorrectAnswers[vocabId].prompt = definitionDoc.data().item : newState.incorrectAnswers[vocabId].answer = definitionDoc.data().item.split("/");
|
||||||
|
})
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
})).catch((error) => {
|
||||||
|
console.log(`Couldn't retrieve incorrect answers: ${error}`);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
await Promise.all(promises);
|
||||||
}
|
}
|
||||||
|
|
||||||
await Promise.all(promises);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setState(newState, () => {
|
this.setState(newState, () => {
|
||||||
if (!setDone) this.answerInput.focus();
|
if (!newState.progressInaccessible && !setDone) this.answerInput.focus();
|
||||||
});
|
});
|
||||||
|
|
||||||
this.props.page.load();
|
this.props.page.load();
|
||||||
@@ -230,6 +244,7 @@ export default withRouter(class Progress extends React.Component {
|
|||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
this.isMounted = false;
|
this.isMounted = false;
|
||||||
this.props.page.unload();
|
this.props.page.unload();
|
||||||
|
this.unlisten();
|
||||||
}
|
}
|
||||||
|
|
||||||
showSettings = () => {
|
showSettings = () => {
|
||||||
@@ -277,7 +292,7 @@ export default withRouter(class Progress extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
showNextItem = () => {
|
proceed = () => {
|
||||||
if (this.state.canProceed) {
|
if (this.state.canProceed) {
|
||||||
if (this.state.currentAnswerStatus === null) {
|
if (this.state.currentAnswerStatus === null) {
|
||||||
this.processAnswer();
|
this.processAnswer();
|
||||||
@@ -342,6 +357,7 @@ export default withRouter(class Progress extends React.Component {
|
|||||||
loading: false,
|
loading: false,
|
||||||
canProceed: true,
|
canProceed: true,
|
||||||
typo: false,
|
typo: false,
|
||||||
|
canStartTest: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (data.correct) {
|
if (data.correct) {
|
||||||
@@ -519,11 +535,98 @@ export default withRouter(class Progress extends React.Component {
|
|||||||
return `${hours}:${minutes}:${seconds}`;
|
return `${hours}:${minutes}:${seconds}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
startLoading = () => {
|
||||||
|
this.setState({
|
||||||
|
canStartTest: false,
|
||||||
|
loading: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
stopLoading = () => {
|
||||||
|
this.setState({
|
||||||
|
canStartTest: true,
|
||||||
|
loading: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
recreateSameTest = () => {
|
||||||
|
if (!this.state.loading) {
|
||||||
|
this.state.functions.createProgress({
|
||||||
|
sets: this.state.setIds,
|
||||||
|
switch_language: this.state.switchLanguage,
|
||||||
|
mode: this.state.mode,
|
||||||
|
limit: this.state.mode === "questions" ? this.state.progress - this.state.incorrect
|
||||||
|
: this.state.mode === "lives" ? this.state.lives
|
||||||
|
: 1,
|
||||||
|
}).then((result) => {
|
||||||
|
const progressId = result.data;
|
||||||
|
this.stopLoading();
|
||||||
|
this.props.history.push("/progress/" + progressId);
|
||||||
|
|
||||||
|
this.props.logEvent("restart_test", {
|
||||||
|
progress_id: progressId,
|
||||||
|
});
|
||||||
|
}).catch((error) => {
|
||||||
|
console.log(`Couldn't start test: ${error}`);
|
||||||
|
this.stopLoading();
|
||||||
|
});
|
||||||
|
|
||||||
|
this.startLoading();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
createTestWithIncorrect = () => {
|
||||||
|
if (!this.state.loading) {
|
||||||
|
this.state.functions.createProgressWithIncorrect(this.props.match.params.progressId).then((result) => {
|
||||||
|
const progressId = result.data;
|
||||||
|
this.stopLoading();
|
||||||
|
this.props.history.push("/progress/" + progressId);
|
||||||
|
|
||||||
|
this.props.logEvent("start_test_with_incorrect", {
|
||||||
|
progress_id: progressId,
|
||||||
|
});
|
||||||
|
}).catch((error) => {
|
||||||
|
console.log(`Couldn't create test with incorrect answers: ${error}`);
|
||||||
|
this.stopLoading();
|
||||||
|
});
|
||||||
|
|
||||||
|
this.startLoading();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
showTestRestart = () => {
|
||||||
|
if (this.state.canStartTest) {
|
||||||
|
this.setState({
|
||||||
|
showTestRestart: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hideTestRestart = () => {
|
||||||
|
this.setState({
|
||||||
|
showTestRestart: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
showIncorrectTestStart = () => {
|
||||||
|
if (this.state.canStartTest) {
|
||||||
|
this.setState({
|
||||||
|
showIncorrectTestStart: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hideIncorrectTestStart = () => {
|
||||||
|
this.setState({
|
||||||
|
showIncorrectTestStart: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{
|
{
|
||||||
this.state.pageLoaded &&
|
this.props.page.loaded &&
|
||||||
(this.state.progressInaccessible
|
(this.state.progressInaccessible
|
||||||
?
|
?
|
||||||
<Error404 />
|
<Error404 />
|
||||||
@@ -537,7 +640,7 @@ export default withRouter(class Progress extends React.Component {
|
|||||||
<div>
|
<div>
|
||||||
<p className="current-prompt">{this.state.currentPrompt}</p>
|
<p className="current-prompt">{this.state.currentPrompt}</p>
|
||||||
<form className="answer-input-container" onSubmit={(e) => e.preventDefault()} >
|
<form className="answer-input-container" onSubmit={(e) => e.preventDefault()} >
|
||||||
<input type="submit" className="form-submit" onClick={this.showNextItem} />
|
<input type="submit" className="form-submit" onClick={this.proceed} />
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
name="answer_input"
|
name="answer_input"
|
||||||
@@ -632,6 +735,23 @@ export default withRouter(class Progress extends React.Component {
|
|||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className="progress-end-button-container">
|
||||||
|
<Button
|
||||||
|
onClick={this.showTestRestart}
|
||||||
|
>
|
||||||
|
Restart test
|
||||||
|
</Button>
|
||||||
|
{
|
||||||
|
this.state.incorrect > 0 &&
|
||||||
|
<Button
|
||||||
|
onClick={this.showIncorrectTestStart}
|
||||||
|
>
|
||||||
|
Create test with incorrect
|
||||||
|
</Button>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
{
|
{
|
||||||
this.state.incorrectAnswers && Object.keys(this.state.incorrectAnswers).length > 0 &&
|
this.state.incorrectAnswers && Object.keys(this.state.incorrectAnswers).length > 0 &&
|
||||||
<>
|
<>
|
||||||
@@ -663,15 +783,6 @@ export default withRouter(class Progress extends React.Component {
|
|||||||
<LineChart data={this.state.attemptHistory} currentPointX={this.state.startTime} />
|
<LineChart data={this.state.attemptHistory} currentPointX={this.state.startTime} />
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
|
|
||||||
<div className="progress-end-button-container">
|
|
||||||
<LinkButton
|
|
||||||
to="/"
|
|
||||||
className="progress-end-button"
|
|
||||||
>
|
|
||||||
Done
|
|
||||||
</LinkButton>
|
|
||||||
</div>
|
|
||||||
</main>
|
</main>
|
||||||
:
|
:
|
||||||
<main className="progress-container">
|
<main className="progress-container">
|
||||||
@@ -679,7 +790,7 @@ export default withRouter(class Progress extends React.Component {
|
|||||||
<div>
|
<div>
|
||||||
<p className="current-prompt">{this.state.currentPrompt}</p>
|
<p className="current-prompt">{this.state.currentPrompt}</p>
|
||||||
<form className="answer-input-container answer-input-container--answer-entered" onSubmit={(e) => e.preventDefault()} >
|
<form className="answer-input-container answer-input-container--answer-entered" onSubmit={(e) => e.preventDefault()} >
|
||||||
<input type="submit" className="form-submit" onClick={this.showNextItem} />
|
<input type="submit" className="form-submit" onClick={this.proceed} />
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
name="answer_input"
|
name="answer_input"
|
||||||
@@ -781,6 +892,24 @@ export default withRouter(class Progress extends React.Component {
|
|||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
this.state.showTestRestart &&
|
||||||
|
<ConfirmationDialog
|
||||||
|
yesFunction={this.recreateSameTest}
|
||||||
|
noFunction={this.hideTestRestart}
|
||||||
|
message="Restart test?"
|
||||||
|
loading={this.state.loading}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
{
|
||||||
|
this.state.showIncorrectTestStart &&
|
||||||
|
<ConfirmationDialog
|
||||||
|
yesFunction={this.createTestWithIncorrect}
|
||||||
|
noFunction={this.hideIncorrectTestStart}
|
||||||
|
message="Create test with incorrect answers?"
|
||||||
|
loading={this.state.loading}
|
||||||
|
/>
|
||||||
|
}
|
||||||
</>)
|
</>)
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -53,22 +53,18 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.progress-end-button-container {
|
.progress-end-button-container {
|
||||||
position: fixed;
|
display: flex;
|
||||||
left: 0;
|
flex-direction: row;
|
||||||
right: 0;
|
flex-wrap: wrap;
|
||||||
margin-left: auto;
|
justify-content: flex-start;
|
||||||
margin-right: auto;
|
word-wrap: break-word;
|
||||||
max-width: 1080px;
|
align-items: center;
|
||||||
bottom: 0;
|
column-gap: 8px;
|
||||||
width: 100%;
|
row-gap: 8px;
|
||||||
height: 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.progress-end-button-container > .button {
|
.progress-end-button-container > .button {
|
||||||
margin: 48px;
|
margin: 0;
|
||||||
position: absolute;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.progress-settings-overlay-content {
|
.progress-settings-overlay-content {
|
||||||
@@ -133,12 +129,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@media screen and (max-width: 720px) {
|
|
||||||
.progress-end-button-container > .button {
|
|
||||||
margin: 24px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (max-width: 660px) {
|
@media screen and (max-width: 660px) {
|
||||||
.progress-settings-overlay-content > .settings-themes-container {
|
.progress-settings-overlay-content > .settings-themes-container {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@@ -158,9 +148,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-height: 600px) {
|
@media screen and (max-height: 600px) {
|
||||||
.progress-end-button-container > .button {
|
|
||||||
margin: 24px;
|
|
||||||
}
|
|
||||||
.progress-settings-overlay-content {
|
.progress-settings-overlay-content {
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,6 +31,10 @@ const soundOne = true;
|
|||||||
const vocabTwo = "vocab_02";
|
const vocabTwo = "vocab_02";
|
||||||
const termTwo = "term_02";
|
const termTwo = "term_02";
|
||||||
const definitionTwo = "definition_02";
|
const definitionTwo = "definition_02";
|
||||||
|
const vocabThree = "vocab_03";
|
||||||
|
const termThree = "term_03";
|
||||||
|
const definitionThree = "definition_03";
|
||||||
|
const soundThree = true;
|
||||||
const soundTwo = true;
|
const soundTwo = true;
|
||||||
const groupOne = "group_01";
|
const groupOne = "group_01";
|
||||||
const groupTwo = "group_02";
|
const groupTwo = "group_02";
|
||||||
@@ -39,11 +43,11 @@ const doubleDefinitionTwo = "definition/02";
|
|||||||
const punctuationDefinitionOne = "definition .,()-_'\"01";
|
const punctuationDefinitionOne = "definition .,()-_'\"01";
|
||||||
const progressVocabOne = userOne + "__" + vocabOne;
|
const progressVocabOne = userOne + "__" + vocabOne;
|
||||||
const progressVocabTwo = userOne + "__" + vocabTwo;
|
const progressVocabTwo = userOne + "__" + vocabTwo;
|
||||||
const vocabThree = "vocab_03";
|
|
||||||
const vocabFour = "vocab_04";
|
const vocabFour = "vocab_04";
|
||||||
const progressVocabThree = userOne + "__" + vocabThree;
|
const progressVocabThree = userOne + "__" + vocabThree;
|
||||||
const progressVocabFour = userOne + "__" + vocabFour;
|
const progressVocabFour = userOne + "__" + vocabFour;
|
||||||
const incorrectAnswer = "incorrect";
|
const incorrectAnswer = "incorrect";
|
||||||
|
const progressOne = "progress_01";
|
||||||
|
|
||||||
async function deleteCollection(db, collectionPath, batchSize) {
|
async function deleteCollection(db, collectionPath, batchSize) {
|
||||||
const collectionRef = db.collection(collectionPath);
|
const collectionRef = db.collection(collectionPath);
|
||||||
@@ -2069,6 +2073,8 @@ describe("Parandum Cloud Functions", function () {
|
|||||||
const groupId = await createGroup(groupOne);
|
const groupId = await createGroup(groupOne);
|
||||||
const groupDocId = firestore.collection("groups").doc(groupId);
|
const groupDocId = firestore.collection("groups").doc(groupId);
|
||||||
|
|
||||||
|
await new Promise(res => setTimeout(res, 1000));
|
||||||
|
|
||||||
const snapGroupAfter = await groupDocId.get().then((doc) => doc.data());
|
const snapGroupAfter = await groupDocId.get().then((doc) => doc.data());
|
||||||
|
|
||||||
const userGroupDocId = firestore.collection("users").doc(userOne).collection("groups").doc(groupId);
|
const userGroupDocId = firestore.collection("users").doc(userOne).collection("groups").doc(groupId);
|
||||||
@@ -2091,6 +2097,421 @@ describe("Parandum Cloud Functions", function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("createProgressWithIncorrect correctly creates new progress record from progress record with incorrect answers in questions mode", async () => {
|
||||||
|
const createProgressWithIncorrect = test.wrap(cloudFunctions.createProgressWithIncorrect);
|
||||||
|
|
||||||
|
const vocabIdOne = `${setOne}__${vocabOne}`;
|
||||||
|
const vocabIdTwo = `${setOne}__${vocabTwo}`;
|
||||||
|
const vocabIdThree = `${setTwo}__${vocabThree}`;
|
||||||
|
|
||||||
|
const progressData = {
|
||||||
|
correct: [vocabIdTwo],
|
||||||
|
incorrect: [vocabIdTwo, vocabIdTwo, vocabIdOne],
|
||||||
|
questions: [vocabIdOne, vocabIdTwo, vocabIdThree],
|
||||||
|
duration: null,
|
||||||
|
progress: 1,
|
||||||
|
start_time: 1627308670962,
|
||||||
|
set_title: `${setOne} & ${setTwo}`,
|
||||||
|
uid: userOne,
|
||||||
|
switch_language: false,
|
||||||
|
mode: "questions",
|
||||||
|
current_correct: [],
|
||||||
|
typo: false,
|
||||||
|
setIds: [setOne, setTwo],
|
||||||
|
};
|
||||||
|
const termDataOne = {
|
||||||
|
term: termOne,
|
||||||
|
sound: soundOne,
|
||||||
|
};
|
||||||
|
const termDataTwo = {
|
||||||
|
item: termTwo,
|
||||||
|
sound: soundTwo,
|
||||||
|
};
|
||||||
|
const termDataThree = {
|
||||||
|
term: termThree,
|
||||||
|
sound: soundThree,
|
||||||
|
};
|
||||||
|
const definitionDataOne = {
|
||||||
|
item: definitionOne,
|
||||||
|
sound: soundOne,
|
||||||
|
};
|
||||||
|
const definitionDataTwo = {
|
||||||
|
item: definitionTwo,
|
||||||
|
sound: soundTwo,
|
||||||
|
};
|
||||||
|
const definitionDataThree = {
|
||||||
|
item: definitionThree,
|
||||||
|
sound: soundThree,
|
||||||
|
};
|
||||||
|
|
||||||
|
const progressDocId = firestore.collection("progress").doc(progressOne);
|
||||||
|
|
||||||
|
await progressDocId.set(progressData);
|
||||||
|
await progressDocId
|
||||||
|
.collection("terms").doc(vocabIdOne).set(termDataOne);
|
||||||
|
await progressDocId
|
||||||
|
.collection("terms").doc(vocabIdTwo).set(termDataTwo);
|
||||||
|
await progressDocId
|
||||||
|
.collection("terms").doc(vocabIdThree).set(termDataThree);
|
||||||
|
await progressDocId
|
||||||
|
.collection("definitions").doc(vocabIdOne).set(definitionDataOne);
|
||||||
|
await progressDocId
|
||||||
|
.collection("definitions").doc(vocabIdTwo).set(definitionDataTwo);
|
||||||
|
await progressDocId
|
||||||
|
.collection("definitions").doc(vocabIdThree).set(definitionDataThree);
|
||||||
|
|
||||||
|
const returnData = await createProgressWithIncorrect(progressOne);
|
||||||
|
assert.strictEqual(typeof returnData, "string");
|
||||||
|
await progressDocId.get((doc) => {
|
||||||
|
const data = doc.data();
|
||||||
|
assert.deepStrictEqual(data.correct, []);
|
||||||
|
assert.deepStrictEqual(data.incorrect, []);
|
||||||
|
assert.deepStrictEqual(data.correct, []);
|
||||||
|
hamjest.assertThat(data.questions, hamjest.anyOf(
|
||||||
|
hamjest.is([vocabOne, vocabTwo, vocabThree]),
|
||||||
|
hamjest.is([vocabOne, vocabThree, vocabTwo]),
|
||||||
|
hamjest.is([vocabTwo, vocabThree, vocabOne]),
|
||||||
|
hamjest.is([vocabTwo, vocabOne, vocabThree]),
|
||||||
|
hamjest.is([vocabOne, vocabTwo, vocabThree]),
|
||||||
|
hamjest.is([vocabOne, vocabThree, vocabTwo])
|
||||||
|
));
|
||||||
|
assert.strictEqual(data.duration, null);
|
||||||
|
assert.strictEqual(data.progress, 0);
|
||||||
|
assert.strictEqual(data.start_time, 1627308670962);
|
||||||
|
assert.strictEqual(data.set_title, `${setOne} & ${setTwo}`);
|
||||||
|
assert.strictEqual(data.uid, userOne);
|
||||||
|
assert.strictEqual(data.switch_language, false);
|
||||||
|
assert.strictEqual(data.mode, "questions");
|
||||||
|
assert.deepStrictEqual(data.current_correct, []);
|
||||||
|
assert.strictEqual(data.typo, false);
|
||||||
|
assert.deepStrictEqual(data.setIds, []);
|
||||||
|
|
||||||
|
});
|
||||||
|
await progressDocId
|
||||||
|
.collection("terms").doc(`${setOne}__${vocabOne}`).get()
|
||||||
|
.then((doc) => assert.deepStrictEqual(doc.data(), termDataOne));
|
||||||
|
await progressDocId
|
||||||
|
.collection("terms").doc(`${setOne}__${vocabTwo}`).get()
|
||||||
|
.then((doc) => assert.deepStrictEqual(doc.data(), termDataTwo));
|
||||||
|
await progressDocId
|
||||||
|
.collection("terms").doc(`${setTwo}__${vocabThree}`).get()
|
||||||
|
.then((doc) => assert.deepStrictEqual(doc.data(), termDataThree));
|
||||||
|
await progressDocId
|
||||||
|
.collection("definitions").doc(`${setOne}__${vocabOne}`).get()
|
||||||
|
.then((doc) => assert.deepStrictEqual(doc.data(), definitionDataOne));
|
||||||
|
await progressDocId
|
||||||
|
.collection("definitions").doc(`${setOne}__${vocabTwo}`).get()
|
||||||
|
.then((doc) => assert.deepStrictEqual(doc.data(), definitionDataTwo));
|
||||||
|
await progressDocId
|
||||||
|
.collection("definitions").doc(`${setTwo}__${vocabThree}`).get()
|
||||||
|
.then((doc) => assert.deepStrictEqual(doc.data(), definitionDataThree));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("createProgressWithIncorrect correctly creates new progress record from progress record with incorrect answers in lives mode", async () => {
|
||||||
|
const createProgressWithIncorrect = test.wrap(cloudFunctions.createProgressWithIncorrect);
|
||||||
|
|
||||||
|
const vocabIdOne = `${setOne}__${vocabOne}`;
|
||||||
|
const vocabIdTwo = `${setOne}__${vocabTwo}`;
|
||||||
|
const vocabIdThree = `${setTwo}__${vocabThree}`;
|
||||||
|
|
||||||
|
const progressData = {
|
||||||
|
correct: [vocabIdTwo],
|
||||||
|
incorrect: [vocabIdTwo, vocabIdTwo, vocabIdOne],
|
||||||
|
questions: [vocabIdOne, vocabIdTwo, vocabIdThree],
|
||||||
|
duration: null,
|
||||||
|
progress: 1,
|
||||||
|
start_time: 1627308670962,
|
||||||
|
set_title: `${setOne} & ${setTwo}`,
|
||||||
|
uid: userOne,
|
||||||
|
switch_language: false,
|
||||||
|
mode: "lives",
|
||||||
|
current_correct: [],
|
||||||
|
typo: false,
|
||||||
|
setIds: [setOne, setTwo],
|
||||||
|
lives: 2,
|
||||||
|
start_lives: 5,
|
||||||
|
};
|
||||||
|
const termDataOne = {
|
||||||
|
term: termOne,
|
||||||
|
sound: soundOne,
|
||||||
|
};
|
||||||
|
const termDataTwo = {
|
||||||
|
item: termTwo,
|
||||||
|
sound: soundTwo,
|
||||||
|
};
|
||||||
|
const termDataThree = {
|
||||||
|
term: termThree,
|
||||||
|
sound: soundThree,
|
||||||
|
};
|
||||||
|
const definitionDataOne = {
|
||||||
|
item: definitionOne,
|
||||||
|
sound: soundOne,
|
||||||
|
};
|
||||||
|
const definitionDataTwo = {
|
||||||
|
item: definitionTwo,
|
||||||
|
sound: soundTwo,
|
||||||
|
};
|
||||||
|
const definitionDataThree = {
|
||||||
|
item: definitionThree,
|
||||||
|
sound: soundThree,
|
||||||
|
};
|
||||||
|
|
||||||
|
const progressDocId = firestore.collection("progress").doc(progressOne);
|
||||||
|
|
||||||
|
await progressDocId.set(progressData);
|
||||||
|
await progressDocId
|
||||||
|
.collection("terms").doc(vocabIdOne).set(termDataOne);
|
||||||
|
await progressDocId
|
||||||
|
.collection("terms").doc(vocabIdTwo).set(termDataTwo);
|
||||||
|
await progressDocId
|
||||||
|
.collection("terms").doc(vocabIdThree).set(termDataThree);
|
||||||
|
await progressDocId
|
||||||
|
.collection("definitions").doc(vocabIdOne).set(definitionDataOne);
|
||||||
|
await progressDocId
|
||||||
|
.collection("definitions").doc(vocabIdTwo).set(definitionDataTwo);
|
||||||
|
await progressDocId
|
||||||
|
.collection("definitions").doc(vocabIdThree).set(definitionDataThree);
|
||||||
|
|
||||||
|
const returnData = await createProgressWithIncorrect(progressOne);
|
||||||
|
assert.strictEqual(typeof returnData, "string");
|
||||||
|
await progressDocId.get((doc) => {
|
||||||
|
const data = doc.data();
|
||||||
|
assert.deepStrictEqual(data.correct, []);
|
||||||
|
assert.deepStrictEqual(data.incorrect, []);
|
||||||
|
assert.deepStrictEqual(data.correct, []);
|
||||||
|
hamjest.assertThat(data.questions, hamjest.anyOf(
|
||||||
|
hamjest.is([vocabOne, vocabTwo, vocabThree]),
|
||||||
|
hamjest.is([vocabOne, vocabThree, vocabTwo]),
|
||||||
|
hamjest.is([vocabTwo, vocabThree, vocabOne]),
|
||||||
|
hamjest.is([vocabTwo, vocabOne, vocabThree]),
|
||||||
|
hamjest.is([vocabOne, vocabTwo, vocabThree]),
|
||||||
|
hamjest.is([vocabOne, vocabThree, vocabTwo])
|
||||||
|
));
|
||||||
|
assert.strictEqual(data.duration, null);
|
||||||
|
assert.strictEqual(data.progress, 0);
|
||||||
|
assert.strictEqual(data.start_time, 1627308670962);
|
||||||
|
assert.strictEqual(data.set_title, `${setOne} & ${setTwo}`);
|
||||||
|
assert.strictEqual(data.uid, userOne);
|
||||||
|
assert.strictEqual(data.switch_language, false);
|
||||||
|
assert.strictEqual(data.mode, "questions");
|
||||||
|
assert.deepStrictEqual(data.current_correct, []);
|
||||||
|
assert.strictEqual(data.typo, false);
|
||||||
|
assert.deepStrictEqual(data.setIds, []);
|
||||||
|
assert.strictEqual(data.lives, 5);
|
||||||
|
assert.strictEqual(data.start_lives, 5);
|
||||||
|
});
|
||||||
|
await progressDocId
|
||||||
|
.collection("terms").doc(`${setOne}__${vocabOne}`).get()
|
||||||
|
.then((doc) => assert.deepStrictEqual(doc.data(), termDataOne));
|
||||||
|
await progressDocId
|
||||||
|
.collection("terms").doc(`${setOne}__${vocabTwo}`).get()
|
||||||
|
.then((doc) => assert.deepStrictEqual(doc.data(), termDataTwo));
|
||||||
|
await progressDocId
|
||||||
|
.collection("terms").doc(`${setTwo}__${vocabThree}`).get()
|
||||||
|
.then((doc) => assert.deepStrictEqual(doc.data(), termDataThree));
|
||||||
|
await progressDocId
|
||||||
|
.collection("definitions").doc(`${setOne}__${vocabOne}`).get()
|
||||||
|
.then((doc) => assert.deepStrictEqual(doc.data(), definitionDataOne));
|
||||||
|
await progressDocId
|
||||||
|
.collection("definitions").doc(`${setOne}__${vocabTwo}`).get()
|
||||||
|
.then((doc) => assert.deepStrictEqual(doc.data(), definitionDataTwo));
|
||||||
|
await progressDocId
|
||||||
|
.collection("definitions").doc(`${setTwo}__${vocabThree}`).get()
|
||||||
|
.then((doc) => assert.deepStrictEqual(doc.data(), definitionDataThree));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("createProgressWithIncorrect won't create new progress record when old progress record belongs to different user", async () => {
|
||||||
|
const createProgressWithIncorrect = test.wrap(cloudFunctions.createProgressWithIncorrect);
|
||||||
|
|
||||||
|
const progressData = {
|
||||||
|
correct: [vocabTwo],
|
||||||
|
incorrect: [vocabTwo, vocabTwo, vocabOne],
|
||||||
|
questions: [vocabOne, vocabTwo, vocabThree],
|
||||||
|
duration: null,
|
||||||
|
progress: 1,
|
||||||
|
start_time: 1627308670962,
|
||||||
|
set_title: `${setOne} & ${setTwo}`,
|
||||||
|
uid: userTwo,
|
||||||
|
switch_language: false,
|
||||||
|
mode: "questions",
|
||||||
|
current_correct: [],
|
||||||
|
typo: false,
|
||||||
|
setIds: [setOne, setTwo],
|
||||||
|
};
|
||||||
|
const termDataOne = {
|
||||||
|
term: termOne,
|
||||||
|
sound: soundOne,
|
||||||
|
};
|
||||||
|
const termDataTwo = {
|
||||||
|
item: termTwo,
|
||||||
|
sound: soundTwo,
|
||||||
|
};
|
||||||
|
const termDataThree = {
|
||||||
|
term: termThree,
|
||||||
|
sound: soundThree,
|
||||||
|
};
|
||||||
|
const definitionDataOne = {
|
||||||
|
item: definitionOne,
|
||||||
|
sound: soundOne,
|
||||||
|
};
|
||||||
|
const definitionDataTwo = {
|
||||||
|
item: definitionTwo,
|
||||||
|
sound: soundTwo,
|
||||||
|
};
|
||||||
|
const definitionDataThree = {
|
||||||
|
item: definitionThree,
|
||||||
|
sound: soundThree,
|
||||||
|
};
|
||||||
|
|
||||||
|
const progressDocId = firestore.collection("progress").doc(progressOne);
|
||||||
|
|
||||||
|
await progressDocId.set(progressData);
|
||||||
|
await progressDocId
|
||||||
|
.collection("terms").doc(`${setOne}__${vocabOne}`).set(termDataOne);
|
||||||
|
await progressDocId
|
||||||
|
.collection("terms").doc(`${setOne}__${vocabTwo}`).set(termDataTwo);
|
||||||
|
await progressDocId
|
||||||
|
.collection("terms").doc(`${setTwo}__${vocabThree}`).set(termDataThree);
|
||||||
|
await progressDocId
|
||||||
|
.collection("definitions").doc(`${setOne}__${vocabOne}`).set(definitionDataOne);
|
||||||
|
await progressDocId
|
||||||
|
.collection("definitions").doc(`${setOne}__${vocabTwo}`).set(definitionDataTwo);
|
||||||
|
await progressDocId
|
||||||
|
.collection("definitions").doc(`${setTwo}__${vocabThree}`).set(definitionDataThree);
|
||||||
|
|
||||||
|
firebase.assertFails(createProgressWithIncorrect(progressOne));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("createProgressWithIncorrect won't create new progress record when progress ID argument isn't a string", async () => {
|
||||||
|
const createProgressWithIncorrect = test.wrap(cloudFunctions.createProgressWithIncorrect);
|
||||||
|
|
||||||
|
const progressData = {
|
||||||
|
correct: [vocabTwo],
|
||||||
|
incorrect: [vocabTwo, vocabTwo, vocabOne],
|
||||||
|
questions: [vocabOne, vocabTwo, vocabThree],
|
||||||
|
duration: null,
|
||||||
|
progress: 1,
|
||||||
|
start_time: 1627308670962,
|
||||||
|
set_title: `${setOne} & ${setTwo}`,
|
||||||
|
uid: userTwo,
|
||||||
|
switch_language: false,
|
||||||
|
mode: "questions",
|
||||||
|
current_correct: [],
|
||||||
|
typo: false,
|
||||||
|
setIds: [setOne, setTwo],
|
||||||
|
};
|
||||||
|
const termDataOne = {
|
||||||
|
term: termOne,
|
||||||
|
sound: soundOne,
|
||||||
|
};
|
||||||
|
const termDataTwo = {
|
||||||
|
item: termTwo,
|
||||||
|
sound: soundTwo,
|
||||||
|
};
|
||||||
|
const termDataThree = {
|
||||||
|
term: termThree,
|
||||||
|
sound: soundThree,
|
||||||
|
};
|
||||||
|
const definitionDataOne = {
|
||||||
|
item: definitionOne,
|
||||||
|
sound: soundOne,
|
||||||
|
};
|
||||||
|
const definitionDataTwo = {
|
||||||
|
item: definitionTwo,
|
||||||
|
sound: soundTwo,
|
||||||
|
};
|
||||||
|
const definitionDataThree = {
|
||||||
|
item: definitionThree,
|
||||||
|
sound: soundThree,
|
||||||
|
};
|
||||||
|
|
||||||
|
const progressDocId = firestore.collection("progress").doc("1");
|
||||||
|
|
||||||
|
await progressDocId.set(progressData);
|
||||||
|
await progressDocId
|
||||||
|
.collection("terms").doc(`${setOne}__${vocabOne}`).set(termDataOne);
|
||||||
|
await progressDocId
|
||||||
|
.collection("terms").doc(`${setOne}__${vocabTwo}`).set(termDataTwo);
|
||||||
|
await progressDocId
|
||||||
|
.collection("terms").doc(`${setTwo}__${vocabThree}`).set(termDataThree);
|
||||||
|
await progressDocId
|
||||||
|
.collection("definitions").doc(`${setOne}__${vocabOne}`).set(definitionDataOne);
|
||||||
|
await progressDocId
|
||||||
|
.collection("definitions").doc(`${setOne}__${vocabTwo}`).set(definitionDataTwo);
|
||||||
|
await progressDocId
|
||||||
|
.collection("definitions").doc(`${setTwo}__${vocabThree}`).set(definitionDataThree);
|
||||||
|
|
||||||
|
firebase.assertFails(createProgressWithIncorrect(1));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("createProgressWithIncorrect won't create new progress record when old progress record doesn't exist", async () => {
|
||||||
|
const createProgressWithIncorrect = test.wrap(cloudFunctions.createProgressWithIncorrect);
|
||||||
|
|
||||||
|
firebase.assertFails(createProgressWithIncorrect("invalid"));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("createProgressWithIncorrect won't create new progress record when old progress record has no incorrect answers", async () => {
|
||||||
|
const createProgressWithIncorrect = test.wrap(cloudFunctions.createProgressWithIncorrect);
|
||||||
|
|
||||||
|
const progressData = {
|
||||||
|
correct: [vocabTwo],
|
||||||
|
incorrect: [],
|
||||||
|
questions: [vocabOne, vocabTwo, vocabThree],
|
||||||
|
duration: null,
|
||||||
|
progress: 1,
|
||||||
|
start_time: 1627308670962,
|
||||||
|
set_title: `${setOne} & ${setTwo}`,
|
||||||
|
uid: userOne,
|
||||||
|
switch_language: false,
|
||||||
|
mode: "questions",
|
||||||
|
current_correct: [],
|
||||||
|
typo: false,
|
||||||
|
setIds: [setOne, setTwo],
|
||||||
|
};
|
||||||
|
const termDataOne = {
|
||||||
|
term: termOne,
|
||||||
|
sound: soundOne,
|
||||||
|
};
|
||||||
|
const termDataTwo = {
|
||||||
|
item: termTwo,
|
||||||
|
sound: soundTwo,
|
||||||
|
};
|
||||||
|
const termDataThree = {
|
||||||
|
term: termThree,
|
||||||
|
sound: soundThree,
|
||||||
|
};
|
||||||
|
const definitionDataOne = {
|
||||||
|
item: definitionOne,
|
||||||
|
sound: soundOne,
|
||||||
|
};
|
||||||
|
const definitionDataTwo = {
|
||||||
|
item: definitionTwo,
|
||||||
|
sound: soundTwo,
|
||||||
|
};
|
||||||
|
const definitionDataThree = {
|
||||||
|
item: definitionThree,
|
||||||
|
sound: soundThree,
|
||||||
|
};
|
||||||
|
|
||||||
|
const progressDocId = firestore.collection("progress").doc(progressOne);
|
||||||
|
|
||||||
|
await progressDocId.set(progressData);
|
||||||
|
await progressDocId
|
||||||
|
.collection("terms").doc(`${setOne}__${vocabOne}`).set(termDataOne);
|
||||||
|
await progressDocId
|
||||||
|
.collection("terms").doc(`${setOne}__${vocabTwo}`).set(termDataTwo);
|
||||||
|
await progressDocId
|
||||||
|
.collection("terms").doc(`${setTwo}__${vocabThree}`).set(termDataThree);
|
||||||
|
await progressDocId
|
||||||
|
.collection("definitions").doc(`${setOne}__${vocabOne}`).set(definitionDataOne);
|
||||||
|
await progressDocId
|
||||||
|
.collection("definitions").doc(`${setOne}__${vocabTwo}`).set(definitionDataTwo);
|
||||||
|
await progressDocId
|
||||||
|
.collection("definitions").doc(`${setTwo}__${vocabThree}`).set(definitionDataThree);
|
||||||
|
|
||||||
|
firebase.assertFails(createProgressWithIncorrect(progressOne));
|
||||||
|
});
|
||||||
|
|
||||||
/*xit("getGroupMembers returns group members correctly", async () => {
|
/*xit("getGroupMembers returns group members correctly", async () => {
|
||||||
const getGroupMembers = test.wrap(cloudFunctions.getGroupMembers);
|
const getGroupMembers = test.wrap(cloudFunctions.getGroupMembers);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user