commit 4420dffb6136799d74c5dbaf6256ad876a0d5543
parent 1167d728c4f8731b1d2ca9d215ff13b16db01bc0
Author: AsherMorgan <59518073+AsherMorgan@users.noreply.github.com>
Date: Wed, 16 Sep 2020 10:17:09 -0700
Merge pull request #19 from AsherMorgan/refactor-quizzer
Refactor quizzer
Diffstat:
8 files changed, 952 insertions(+), 363 deletions(-)
diff --git a/Scripts/Home.js b/Scripts/Home.js
@@ -13,14 +13,13 @@ function loadVue() {
darkTheme: false,
verbFilters: [],
vocabFilters: [],
+ errorMsg: "",
promptType: localStorage.getItem("promptType") || "Text",
inputType: localStorage.getItem("inputType") || "Text",
repeatPrompts: localStorage.getItem("repeatPrompts") || "Never",
prompts: [],
promptIndex: 0,
- responce: "",
- responceActive: true,
},
methods: {
@@ -141,6 +140,23 @@ function loadVue() {
else {
return "en";
}
+ },
+ updateProgress: function(prompts, index) {
+ // Get localStorage prefix
+ let prefix;
+ if (app.state === "vocabSettings" || app.state === "vocabQuizzer") {
+ prefix = "vocab-"
+ }
+ else if (app.state === "verbSettings" || app.state === "verbQuizzer") {
+ prefix = "verb-"
+ }
+ else {
+ return;
+ }
+
+ // Save progress to local storage
+ localStorage.setItem(prefix + "prompts", JSON.stringify(prompts));
+ localStorage.setItem(prefix + "prompt", JSON.stringify(index));
}
},
@@ -175,17 +191,10 @@ function loadVue() {
},
repeatPrompts: function(value) {
localStorage.setItem("repeatPrompts", value);
- }
- },
-
- computed: {
- prompt: function() {
- if (this.promptIndex < this.prompts.length) {
- return this.prompts[this.promptIndex];
- }
- else {
- return ["", "", "", ""];
- }
+ },
+ state: function() {
+ // Reset error message
+ app.errorMsg = "";
}
},
diff --git a/Scripts/Quizzer.js b/Scripts/Quizzer.js
@@ -1,242 +1,291 @@
-// Declare global variables
-let Prefix; // Dictionary of quizzer settings
-
-
-
-// Reads a peice of text
-function Read(text, label)
-{
- var msg = new SpeechSynthesisUtterance(text);
- if (label.toLowerCase().includes("english")) {
- msg.lang = 'en';
- }
- else if (label.toLowerCase().includes("spanish")){
- msg.lang = 'es';
- }
- window.speechSynthesis.speak(msg);
-}
-
-
-
-// Shuffles a list of items
-function Shuffle(items) {
- // Initialize variables
- var currentIndex = items.length;
- var temp;
- var randomIndex;
-
- // While there are more elements to shuffle
- while (0 !== currentIndex) {
- // Pick a remaining element
- randomIndex = Math.floor(Math.random() * currentIndex);
- currentIndex--;
-
- // Swap the two elements
- temp = items[currentIndex];
- items[currentIndex] = items[randomIndex];
- items[randomIndex] = temp;
- }
-
- // Return shuffled items
- return items;
-}
-
+let quizzer = Vue.component("quizzer", {
+ props: {
+ active: {
+ type: Boolean,
+ default: false,
+ },
+
+ startingPrompts: {
+ type: Array,
+ default: function() {
+ return [];
+ },
+ },
+ startingIndex: {
+ type: Number,
+ default: 0,
+ },
+
+ promptType: {
+ type: String,
+ default: "Text",
+ },
+ inputType: {
+ type: String,
+ default: "Text",
+ },
+ repeatPrompts: {
+ type: String,
+ default: "Never",
+ },
+ },
+
+ data: function() {
+ return {
+ prompts: this.startingPrompts,
+ index: this.startingIndex,
+ responce: "",
+ responceActive: true,
+ congratsActive: false,
+ };
+ },
+
+ watch: {
+ active: function(value) {
+ if (value) {
+ // Update prompts
+ this.prompts = this.startingPrompts;
+ this.index = this.startingIndex - 1;
+
+ // Reset quizzer
+ this.Reset();
+ }
+ },
+ },
+
+ methods: {
+ // Give the user a new prompt
+ Reset: function() {
+ // Check is Quizzer is active
+ if (!this.active) {
+ return;
+ }
+ // Show and hide elements
+ this.responceActive = true;
+ this.congratsActive = false;
+
+ // Get new prompt
+ this.index++;
+ if (this.index == this.prompts.length) {
+ // The user just finished
+ this.prompts = Shuffle(this.prompts);
+ this.index = 0;
+ this.congratsActive = true;
+ }
-// Starts the quizzer
-function StartQuizzer(prefix) {
- // Set variables and settings
- app.promptIndex--;
- Prefix = prefix;
+ // Emit new-prompt event
+ this.$emit("new-prompt", this.prompts, this.index);
- // Validate Terms
- if (!app.prompts || isNaN(app.promptIndex) || app.promptIndex < -1 || app.promptIndex > app.prompts.length) {
- throw "Bad arguments.";
- }
- else if (app.prompts.length == 0) {
- throw "Terms is empty.";
- }
-
- // Validate browser for voice input
- if (app.inputType != "Text") {
- if (typeof InstallTrigger !== "undefined") {
- // Browser is Firefox
- alert("You must enable speech recognition in about:config.");
- }
- else if (!window.chrome || (!window.chrome.webstore && !window.chrome.runtime)) {
- // Browser is not Googole Chrome or Microsoft (Chromium) Edge
- alert("Your browser does not support voice input.");
- return;
- }
- }
+ // Reset responce
+ this.responce = "";
- // Save terms to local storage
- localStorage.setItem(Prefix + "prompts", JSON.stringify(app.prompts));
-
- // Give iOS devices ringer warning for prompt audio
- if (app.promptType != "Text") {
- if (!!navigator.platform && /iPad|iPhone|iPod/.test(navigator.platform)) {
- alert("Please make sure your ringer is on in order to hear audio prompts.");
- }
- }
+ // Read prompt
+ if (this.promptType != "Text") {
+ Read(this.prompt[1], this.prompt[0]);
+ }
- // Give the user a prompt
- Reset();
-}
+ // Get voice input
+ if (this.inputType != "Text") {
+ // Create recognition object
+ var recognition = new (window.SpeechRecognition || window.webkitSpeechRecognition || window.mozSpeechRecognition || window.msSpeechRecognition)();
+
+ // Set language
+ if (this.prompt[2].toLowerCase().includes("english")) {
+ recognition.lang = 'en-US';
+ }
+ else if (this.prompt[2].toLowerCase().includes("spanish")) {
+ recognition.lang = 'es-mx';
+ }
+
+ // Set options
+ recognition.continuous = true;
+ recognition.interimResults = false;
+ recognition.maxAlternatives = 16;
+
+ // Start listening
+ recognition.start();
+ recognition.onresult = (event) => {
+ let parsed_responce = ""
+ for (var result of event.results[0]) {
+ parsed_responce += `${result.transcript}, `;
+ parsed_responce += `${result.transcript.split(" or ").join(", ")}, `;
+ }
+ this.responce = parsed_responce;
+ this.Submit();
+ };
+ }
+ },
+ // Processes a user's submitted responce
+ Submit: function() {
+ // Check is Quizzer is active
+ if (!this.active) {
+ return;
+ }
+ // Parse responce
+ var responce = this.responce.toLowerCase(); // Make responce lowercase
+ responce = responce.replace(/a`/g, "á"); // Apply accented a shortcut
+ responce = responce.replace(/e`/g, "é"); // Apply accented e shortcut
+ responce = responce.replace(/i`/g, "í"); // Apply accented i shortcut
+ responce = responce.replace(/n`/g, "ñ"); // Apply n with tilde shortcut
+ responce = responce.replace(/n~/g, "ñ"); // Apply n with tilde shortcut
+ responce = responce.replace(/o`/g, "ó"); // Apply accented o shortcut
+ responce = responce.replace(/u`/g, "ú"); // Apply accented u shortcut
+ responce = responce.replace(/u~/g, "ü"); // Apply u with diaeresis shortcut
+ var responces = responce.split(","); // Split string by commas
+ for (var i = 0; i < responces.length; i++) {
+ responces[i] = responces[i].split(" ").filter(function(x){return x !== "";}).join(" "); // Trim whitespace
+ }
-// Give the user a new prompt
-function Reset() {
- // Show and hide elements
- document.getElementById("quizzerCongrats").hidden = true;
- document.getElementById("quizzerInput").focus();
- app.responceActive = true;
-
- // Get new prompt
- app.promptIndex++;
- if (app.promptIndex == app.prompts.length) {
- // The user just finished
- app.prompts = Shuffle(app.prompts);
- app.promptIndex = 0;
- document.getElementById("quizzerCongrats").hidden = false;
- }
+ // Parse answer
+ let answers = this.prompt[3].toLowerCase().split(","); // Split string by commas
+ for (var i = 0; i < answers.length; i++) {
+ answers[i] = answers[i].trim(); // Trim whitespace
+ }
- // Save progress to local storage
- localStorage.setItem(Prefix + "prompt", app.promptIndex);
+ // Check responce
+ var correct = true;
+ for (var answer of answers) {
+ if (!responces.includes(answer)) {
+ correct = false;
+ }
+ }
- // Reset responce
- app.responce = "";
+ // Give user feedback
+ if (!correct) {
+ // Show and hide elements
+ this.congratsActive = false;
+ this.responceActive = false;
+ try {
+ // Will fail if not mounted
+ this.$refs.feedback.scrollIntoView(false);
+ this.$refs.input.focus();
+ }
+ catch { }
+ }
+ else {
+ // Responce was correct
+ this.Reset();
+ }
+ },
- // Read prompt
- if (app.promptType != "Text") {
- Read(app.prompt[1], app.prompt[0]);
- }
+ // Processes an incorrect responce and then resets the quizzer
+ Continue: function() {
+ // Check is Quizzer is active
+ if (!this.active) {
+ return;
+ }
+
+ // Repeat prompt
+ switch (this.repeatPrompts)
+ {
+ case "Never":
+ // Don't repeat
+ break;
+ case "Immediately":
+ // Repeat imitiately
+ this.index--;
+ break;
+ case "5 prompts later":
+ // Repeat 5 prompts later
+ var temp = this.prompt;
+ this.prompts.splice(this.index, 1);
+ this.prompts.splice(this.index + 5, 0, temp);
+ this.index--;
+ break;
+ case "At the end":
+ // Repeat at end of Terms
+ var temp = this.prompt;
+ this.prompts.splice(this.index, 1);
+ this.prompts.push(temp);
+ this.index--;
+ break;
+ }
- // Get voice input
- if (app.inputType != "Text") {
- // Create recognition object
- var recognition = new (window.SpeechRecognition || window.webkitSpeechRecognition || window.mozSpeechRecognition || window.msSpeechRecognition)();
+ // Reset quizzer
+ this.Reset();
+ },
- // Set language
- if (app.prompt[2].toLowerCase().includes("english")) {
- recognition.lang = 'en-US';
- }
- else if (app.prompt[2].toLowerCase().includes("spanish")) {
- recognition.lang = 'es-mx';
- }
-
- // Set options
- recognition.continuous = true;
- recognition.interimResults = false;
- recognition.maxAlternatives = 16;
-
- // Start listening
- recognition.start();
- recognition.onresult = function(event) {
- responce = ""
- for (var result of event.results[0]) {
- responce += `${result.transcript}, `;
- responce += `${result.transcript.split(" or ").join(", ")}, `;
- }
- app.responce = responce;
- Submit()
- };
- }
-}
-
-
-
-// Processes a user's submitted responce
-function Submit() {
- // Parse responce
- var responce = app.responce.toLowerCase(); // Make responce lowercase
- responce = responce.replace(/a`/g, "á"); // Apply accented a shortcut
- responce = responce.replace(/e`/g, "é"); // Apply accented e shortcut
- responce = responce.replace(/i`/g, "í"); // Apply accented i shortcut
- responce = responce.replace(/n`/g, "ñ"); // Apply n with tilde shortcut
- responce = responce.replace(/n~/g, "ñ"); // Apply n with tilde shortcut
- responce = responce.replace(/o`/g, "ó"); // Apply accented o shortcut
- responce = responce.replace(/u`/g, "ú"); // Apply accented u shortcut
- responce = responce.replace(/u~/g, "ü"); // Apply u with diaeresis shortcut
- var responces = responce.split(","); // Split string by commas
- for (var i = 0; i < responces.length; i++) {
- responces[i] = responces[i].split(" ").filter(function(x){return x !== "";}).join(" "); // Trim whitespace
- }
-
- // Parse answer
- let answers = app.prompt[3].toLowerCase().split(","); // Split string by commas
- for (var i = 0; i < answers.length; i++) {
- answers[i] = answers[i].trim(); // Trim whitespace
- }
+ // Called when the user hits enter or presses the enter button
+ Enter: function() {
+ // Check is Quizzer is active
+ if (!this.active) {
+ return;
+ }
+
+ if (this.responceActive) {
+ this.Submit();
+ }
+ else {
+ this.Continue();
+ }
+ },
+
+ getLang: function(label) {
+ if (label.toLowerCase().includes("spanish")) {
+ return "es";
+ }
+ else {
+ return "en";
+ }
+ },
+ },
- // Check responce
- var correct = true;
- for (var answer of answers) {
- if (!responces.includes(answer)) {
- correct = false;
+ computed: {
+ prompt: function() {
+ if (this.index < this.prompts.length) {
+ return this.prompts[this.index];
+ }
+ else {
+ return ["", "", "", ""];
+ }
}
- }
-
- // Give user feedback
- if (!correct) {
- // Show and hide elements
- document.getElementById("quizzerCongrats").hidden = true;
- document.getElementById("quizzerFeedback").scrollIntoView(false);
- document.getElementById("quizzerInput").focus();
- app.responceActive = false;
- }
- else {
- // Responce was correct
- Reset();
- }
-}
-
-
-
-// Processes an incorrect responce and then resets the quizzer
-function Continue() {
- // Repeat prompt
- switch (app.repeatPrompts)
- {
- case "Never":
- // Don't repeat
- break;
- case "Immediately":
- // Repeat imitiately
- app.promptIndex--;
- break;
- case "5 prompts later":
- // Repeat 5 prompts later
- var temp = app.prompt;
- app.prompts.splice(app.promptIndex, 1);
- app.prompts.splice(app.promptIndex + 5, 0, temp);
- app.promptIndex--;
- break;
- case "At the end":
- // Repeat at end of Terms
- var temp = app.prompt;
- app.prompts.splice(app.promptIndex, 1);
- app.prompts.push(temp);
- app.promptIndex--;
- break;
- }
+ },
- // Save terms to local storage
- localStorage.setItem(Prefix + "terms", JSON.stringify(app.prompts));
-
- // Reset quizzer
- Reset();
-}
+ template: `
+ <div>
+ <p id="quizzerProgress">{{ index }} / {{ prompts.length }}</p>
+
+ <section>
+ <label id="quizzerPromptType" for="quizzerPrompt" :lang="getLang(prompt[0])">{{ prompt[0] }}</label>
+ <span id="quizzerPrompt" @click="Read(prompt[1], prompt[0]);">{{ prompt[1] }}</span>
+ </section>
+
+ <section>
+ <label id="quizzerInputType" for="quizzerInput">{{ prompt[2] }}</label>
+ <input id="quizzerInput" ref="input" type="text" v-model="responce" :readonly="!responceActive || inputType == 'Voice'"
+ @keyup.ctrl.enter.exact="Reset();" @keyup.enter.exact="Enter();" :lang="getLang(prompt[2])"
+ autocomplete="off" spellcheck="false" autocorrect="off" placeholder="Type the answer">
+ </section>
+
+ <div id="quizzerButtons">
+ <button v-if="responceActive" :disabled="inputType == 'Voice'" @click="Submit();">Submit</button>
+ <button v-else @click="Continue();">Continue</button>
+ <button @click="Reset();">Skip</button>
+ </div>
+
+ <div id="quizzerFeedback" ref="feedback" v-show="!responceActive" class="bad">
+ The correct answer is
+ <span id="quizzerFeedbackTerm" @click="Read(prompt[3], prompt[2]);">{{ prompt[3].toLowerCase() }}</span>.
+ </div>
+ <div id="quizzerCongrats" class="good" v-show="congratsActive">Congratulations! You made it back to the beginning!</div>
+ </div>
+ `,
+});
-// Called when the user hits enter or presses the enter button
-function Enter() {
- if (app.responceActive) {
- Submit();
+// Reads a peice of text
+function Read(text, label)
+{
+ var msg = new SpeechSynthesisUtterance(text);
+ if (label.toLowerCase().includes("english")) {
+ msg.lang = 'en';
}
- else {
- Continue();
+ else if (label.toLowerCase().includes("spanish")){
+ msg.lang = 'es';
}
+ window.speechSynthesis.speak(msg);
}
diff --git a/Scripts/Settings.js b/Scripts/Settings.js
@@ -1,7 +1,6 @@
// Start a new session
function CreateSession() {
- // Get prompts and localStorage prefix
- let prefix;
+ // Get prompts
if (app.state == "vocabSettings") {
// Filter and load Sets into prompts
app.prompts = [];
@@ -13,16 +12,10 @@ function CreateSession() {
// Shuffle prompts
app.prompts = Shuffle(app.prompts);
-
- // Set prefix
- prefix = "vocab-"
}
else if (app.state == "verbSettings") {
// Get prompts
app.prompts = Shuffle(ApplyVerbFilter(Sets["Verbs"], app.verbFilters));
-
- // Set prefix
- prefix = "verb-"
}
// Set progress
@@ -30,27 +23,18 @@ function CreateSession() {
// Start quizzer
try {
- // Start quizzer
- StartQuizzer(prefix);
-
- // Show and hide elements
- if (app.state == "verbSettings") {
- app.state = "verbQuizzer";
- }
- if (app.state == "vocabSettings") {
- app.state = "vocabQuizzer";
- }
+ StartSession();
}
catch (e) {
switch (e) {
case "Terms is empty.":
- document.getElementById("settingsError").textContent = "Your custom vocabulary set must contain at least one term.";
+ app.errorMsg = "Your custom vocabulary set must contain at least one term.";
document.getElementById("settingsError").scrollIntoView(false);
break;
default:
- document.getElementById("settingsError").textContent = "An error occured.";
+ app.errorMsg = "An error occured.";
document.getElementById("settingsError").scrollIntoView(false);
- break;
+ throw e;
}
}
}
@@ -74,32 +58,68 @@ function ResumeSession() {
// Start quizzer
try {
- StartQuizzer(prefix);
-
- // Show and hide elements
- if (app.state == "verbSettings") {
- app.state = "verbQuizzer";
- }
- if (app.state == "vocabSettings") {
- app.state = "vocabQuizzer";
- }
+ StartSession();
}
catch (e) {
switch (e) {
case "Bad arguments.":
- document.getElementById("settingsError").textContent = "An error occured while resuming the previous session.";
+ app.errorMsg = "An error occured while resuming the previous session.";
document.getElementById("settingsError").scrollIntoView(false);
break;
case "Terms is empty.":
- document.getElementById("settingsError").textContent = "Your custom vocabulary set must contain at least one term.";
+ app.errorMsg = "Your custom vocabulary set must contain at least one term.";
document.getElementById("settingsError").scrollIntoView(false);
break;
default:
- document.getElementById("settingsError").textContent = "An error occured.";
+ app.errorMsg = "An error occured.";
document.getElementById("settingsError").scrollIntoView(false);
- break;
+ throw e;
+ }
+ }
+}
+
+
+
+// Performs validations and then starts the quizzer
+function StartSession() {
+ // Validate prompts and promptIndex
+ if (!app.prompts) {
+ throw "Bad arguments.";
+ }
+ else if (app.prompts.length == 0) {
+ throw "Terms is empty.";
+ }
+ else if (isNaN(app.promptIndex) || app.promptIndex < 0 || app.promptIndex >= app.prompts.length) {
+ throw "Bad arguments.";
+ }
+
+ // Validate browser for voice input
+ if (app.inputType != "Text") {
+ if (typeof InstallTrigger !== "undefined") {
+ // Browser is Firefox
+ alert("You must enable speech recognition in about:config.");
+ }
+ else if (!window.chrome || (!window.chrome.webstore && !window.chrome.runtime)) {
+ // Browser is not Googole Chrome or Microsoft (Chromium) Edge
+ alert("Your browser does not support voice input.");
+ return;
}
}
+
+ // Give iOS devices ringer warning for prompt audio
+ if (app.promptType != "Text") {
+ if (!!navigator.platform && /iPad|iPhone|iPod/.test(navigator.platform)) {
+ alert("Please make sure your ringer is on in order to hear audio prompts.");
+ }
+ }
+
+ // Show and hide elements (also enables the quizzer)
+ if (app.state == "verbSettings") {
+ app.state = "verbQuizzer";
+ }
+ else if (app.state == "vocabSettings") {
+ app.state = "vocabQuizzer";
+ }
}
@@ -290,3 +310,28 @@ function ApplyVerbFilter(terms, filterInfo) {
}
return results;
}
+
+
+
+// Shuffles a list of items
+function Shuffle(items) {
+ // Initialize variables
+ var currentIndex = items.length;
+ var temp;
+ var randomIndex;
+
+ // While there are more elements to shuffle
+ while (0 !== currentIndex) {
+ // Pick a remaining element
+ randomIndex = Math.floor(Math.random() * currentIndex);
+ currentIndex--;
+
+ // Swap the two elements
+ temp = items[currentIndex];
+ items[currentIndex] = items[randomIndex];
+ items[randomIndex] = temp;
+ }
+
+ // Return shuffled items
+ return items;
+}
diff --git a/Tests/index.html b/Tests/index.html
@@ -13,6 +13,9 @@
<body>
<div id="mocha"></div>
+ <!-- To prevent document.getElementById errors -->
+ <div id="app" hidden></div>
+
<!-- Scripts being tested -->
<script src="../Scripts/Home.js"></script>
<script src="../Scripts/Quizzer.js"></script>
diff --git a/Tests/test.app.js b/Tests/test.app.js
@@ -8,6 +8,7 @@ describe("App", function() {
it("State should be 'home'", function() {
expect(app.state).to.equal("home");
});
+
it("VerFilters should be empty", function() {
expect(app.verbFilters.length).to.equal(0);
});
@@ -16,6 +17,10 @@ describe("App", function() {
expect(app.vocabFilters.length).to.equal(0);
});
+ it("ErrorMsg should be empty", function() {
+ expect(app.errorMsg).to.equal("");
+ });
+
it("Prompts should be empty", function() {
expect(app.vocabFilters.length).to.equal(0);
});
@@ -23,14 +28,6 @@ describe("App", function() {
it("PromptIndex should be 0", function() {
expect(app.promptIndex).to.equal(0);
});
-
- it("Responce should be empty", function() {
- expect(app.responce).to.equal("");
- });
-
- it("ResponceActive should be true", function() {
- expect(app.responceActive).to.equal(true);
- });
});
describe("Back method", function() {
@@ -313,53 +310,6 @@ describe("App", function() {
})
});
- describe("Prompt property", function() {
- it("Should be empty if there aren't any prompt", function() {
- // Assert prompts and promptIndex are correct
- expect(app.promptIndex).to.equal(0);
- expect(app.prompts.length).to.equal(0);
-
- // Assert prompt is empty
- expect(app.prompt.length).to.equal(4);
- expect(app.prompt[0]).to.equal("");
- expect(app.prompt[1]).to.equal("");
- expect(app.prompt[2]).to.equal("");
- expect(app.prompt[3]).to.equal("");
- });
-
- it("Should be empty if promptIndex is invalid", function() {
- // Initialize promptIndex
- app.promptIndex = 2;
-
- // Assert prompts is correct
- expect(app.prompts.length).to.equal(0);
-
- // Assert prompt is empty
- expect(app.prompt.length).to.equal(4);
- expect(app.prompt[0]).to.equal("");
- expect(app.prompt[1]).to.equal("");
- expect(app.prompt[2]).to.equal("");
- expect(app.prompt[3]).to.equal("");
- });
-
- it("Should be the current prompt if promptIndex is valid", function() {
- // Initialize prompts and promptIndex
- app.promptIndex = 1;
- app.prompts = [
- ["a1", "b1", "c1", "d1"],
- ["a2", "b2", "c2", "d2"],
- ["a3", "b3", "c3", "d3"],
- ];
-
- // Assert prompt is correct
- expect(app.prompt.length).to.equal(4);
- expect(app.prompt[0]).to.equal("a2");
- expect(app.prompt[1]).to.equal("b2");
- expect(app.prompt[2]).to.equal("c2");
- expect(app.prompt[3]).to.equal("d2");
- });
- });
-
describe("PromptType watch", function() {
it("Should update setting in localStorage", async function() {
// Save original setting from localStorage
diff --git a/Tests/test.quizzer.js b/Tests/test.quizzer.js
@@ -1,17 +1,514 @@
describe("Quizzer", function() {
- describe("Shuffle method", function() {
- it("Should not alter list", function() {
- // Initialize list
- let list1 = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o"]; // 15 items
-
- // Shuffle list
- let list2 = Shuffle(list1);
-
- // Assert list shuffled
- expect(list2.length).to.equal(list1.length);
- for (let item of list2) {
- expect(list1.includes(item)).to.equal(true);
- }
+ let Quizzer;
+ beforeEach(function() {
+ // Create quizzer component
+ Quizzer = new quizzer();
+ });
+
+ describe("Initial state", function() {
+ it("Active should be false", function() {
+ expect(Quizzer.active).to.equal(false);
+ });
+
+ it("StartingPrompts should be empty", function() {
+ expect(Quizzer.startingPrompts.length).to.equal(0);
+ });
+
+ it("StartingIndex should be 0", function() {
+ expect(Quizzer.startingIndex).to.equal(0);
+ });
+
+ it("PromptType should be text", function() {
+ expect(Quizzer.promptType).to.equal("Text");
+ });
+
+ it("InputType should be text", function() {
+ expect(Quizzer.inputType).to.equal("Text");
+ });
+
+ it("RepeatPrompts should be never", function() {
+ expect(Quizzer.repeatPrompts).to.equal("Never");
+ });
+
+ it("Prompts should be empty", function() {
+ expect(Quizzer.prompts.length).to.equal(0);
+ });
+
+ it("Index should be 0", function() {
+ expect(Quizzer.index).to.equal(0);
+ });
+
+ it("Responce should be empty", function() {
+ expect(Quizzer.responce).to.equal("");
+ });
+
+ it("ResponceActive should be true", function() {
+ expect(Quizzer.responceActive).to.equal(true);
+ });
+
+ it("CongratsActive should be true", function() {
+ expect(Quizzer.congratsActive).to.equal(false);
+ });
+ });
+
+ describe("Reset method", function() {
+ it("Shouldn't do anything if active is false", function() {
+ // Run Reset
+ Quizzer.Reset();
+
+ // Assert nothing changed
+ expect(Quizzer.prompts.length).to.equal(0);
+ expect(Quizzer.index).to.equal(0);
+ expect(Quizzer.responce).to.equal("");
+ expect(Quizzer.responceActive).to.equal(true);
+ expect(Quizzer.congratsActive).to.equal(false);
+ });
+
+ it("Should set responceActive to true", function() {
+ // Initialize variables
+ Quizzer.active = true;
+ Quizzer.responceActive = false;
+
+ // Run reset
+ Quizzer.Reset();
+
+ // Assert responceActive is true
+ expect(Quizzer.responceActive).to.equal(true);
+ });
+
+ it("Should set congratsActive to false", function() {
+ // Initialize variables
+ Quizzer.active = true;
+ Quizzer.congratsActive = true;
+
+ // Run reset
+ Quizzer.Reset();
+
+ // Assert congratsActive is true
+ expect(Quizzer.congratsActive).to.equal(false);
+ });
+
+ it("Should set congratsActive to true if on last term", function() {
+ // Initialize variables
+ Quizzer.active = true;
+ Quizzer.prompts = [["A1", "A2", "A3", "A4"], ["B1", "B2", "B3", "B4"]];
+ Quizzer.index = 1;
+
+ // Run reset
+ Quizzer.Reset();
+
+ // Assert congratsActive is true
+ expect(Quizzer.congratsActive).to.equal(true);
+ });
+
+ it("Should reset responce", function() {
+ // Initialize variables
+ Quizzer.active = true;
+ Quizzer.responce = "test"; // Set to empty string when reset is called
+ expect(Quizzer.responce).to.equal("test");
+
+ // Run reset
+ Quizzer.Reset();
+
+ // Assert reset called
+ expect(Quizzer.responce).to.equal("");
+ });
+ });
+
+ describe("Submit method", function() {
+ beforeEach(function() {
+ // Initialize Vue to avoid document.getElementById errors
+ loadVue();
+ app.state = "verbQuizzer";
+ });
+
+ it("Shouldn't do anything if active is false", function() {
+ // Initialize variables
+ Quizzer.responceActive = "test"; // Will be changed whether or not resopnce is correct
+
+ // Run Submit
+ Quizzer.Submit();
+
+ // Assert nothing changed
+ expect(Quizzer.responceActive).to.equal("test");
+ });
+
+ it("Should call Reset if responce is correct", function() {
+ // Initialize variables
+ Quizzer.active = true;
+ Quizzer.prompts = [["A1", "A2", "A3", "A4"]]
+ Quizzer.responce = "A4";
+
+ // Call Submit
+ Quizzer.Submit();
+
+ // Assert Reset called
+ expect(Quizzer.congratsActive).to.equal(true); // Reset will show congrats
+ });
+
+ it("Should set responceActive to false if responce is incorrect", function() {
+ // Initialize variables
+ Quizzer.active = true;
+ Quizzer.prompts = [["A1", "A2", "A3", "A4"]]
+ Quizzer.responce = "A5";
+
+ // Call Submit
+ Quizzer.Submit();
+
+ // Assert responceActive set to false
+ expect(Quizzer.responceActive).to.equal(false);
+ });
+
+ it("Should accept multiple responces", function() {
+ // Initialize variables
+ Quizzer.active = true;
+ Quizzer.prompts = [["A1", "A2", "A3", "A4"]]
+ Quizzer.responce = "A1, A2, A3, A4";
+
+ // Call Submit
+ Quizzer.Submit();
+
+ // Assert responce accepted
+ expect(Quizzer.congratsActive).to.equal(true); // Reset will show congrats
+ });
+
+ it("Should accept multiple answers", function() {
+ // Initialize variables
+ Quizzer.active = true;
+ Quizzer.prompts = [["A1", "A2", "A3", "A1, A2, A3, A4"]]
+ Quizzer.responce = "A1, A2, A3, A4";
+
+ // Call Submit
+ Quizzer.Submit();
+
+ // Assert answer accepted
+ expect(Quizzer.congratsActive).to.equal(true); // Reset will show congrats
+ });
+
+ it("Should require all answers", function() {
+ // Initialize variables
+ Quizzer.active = true;
+ Quizzer.prompts = [["A1", "A2", "A3", "A1, A2, A3, A4"]]
+ Quizzer.responce = "A1, A2, A3";
+
+ // Call Submit
+ Quizzer.Submit();
+
+ // Assert answer no accepted
+ expect(Quizzer.responceActive).to.equal(false);
+ });
+
+ it("Should accept mixed-case responces", function() {
+ // Initialize variables
+ Quizzer.active = true;
+ Quizzer.prompts = [["A1", "A2", "A3", "A4"]]
+ Quizzer.responce = "a4";
+
+ // Call Submit
+ Quizzer.Submit();
+
+ // Assert responce accepted
+ expect(Quizzer.congratsActive).to.equal(true); // Reset will show congrats
+ });
+
+ it("Should accept responces with extra spaces", function() {
+ // Initialize variables
+ Quizzer.active = true;
+ Quizzer.prompts = [["A1", "A2", "A3", "A4"]]
+ Quizzer.responce = " a4 ";
+
+ // Call Submit
+ Quizzer.Submit();
+
+ // Assert responce accepted
+ expect(Quizzer.congratsActive).to.equal(true); // Reset will show congrats
+ });
+
+ it("Should convert accented characters", function() {
+ // Initialize variables
+ Quizzer.active = true;
+ Quizzer.prompts = [["A1", "A2", "A3", "Á4"]]
+ Quizzer.responce = "a`4";
+
+ // Call Submit
+ Quizzer.Submit();
+
+ // Assert responce accepted
+ expect(Quizzer.congratsActive).to.equal(true); // Reset will show congrats
+ });
+ });
+
+ describe("Continue method", function() {
+ it("Shouldn't do anything if active is false", function() {
+ // Initialize variables
+ Quizzer.prompts = [["A1", "A2", "A3", "A4"], ["B1", "B2", "B3", "B4"]];
+ Quizzer.index = 0;
+ Quizzer.repeatPrompts = "At the end";
+ Quizzer.responceActive = false; // Will be changed if Reset is called
+
+ // Run Continue
+ Quizzer.Continue();
+
+ // Assert nothing changed
+ expect(Quizzer.prompts[0]).to.have.members(["A1", "A2", "A3", "A4"]);
+ expect(Quizzer.prompts[1]).to.have.members(["B1", "B2", "B3", "B4"]);
+ expect(Quizzer.index).to.equal(0);
+ expect(Quizzer.responceActive).to.equal(false);
+ });
+
+ it("Shouldn't change prompts if repeatPrompts is Never", function() {
+ // Initialize variables
+ Quizzer.active = true;
+ Quizzer.prompts = [["A1", "A2", "A3", "A4"], ["B1", "B2", "B3", "B4"]];
+ Quizzer.index = 0;
+ Quizzer.repeatPrompts = "Never";
+
+ // Run Continue
+ Quizzer.Continue();
+
+ // Assert prompts not changed
+ expect(Quizzer.prompts[0]).to.have.members(["A1", "A2", "A3", "A4"]);
+ expect(Quizzer.prompts[1]).to.have.members(["B1", "B2", "B3", "B4"]);
+ expect(Quizzer.index).to.equal(1);
+ });
+
+ it("Shouldn't change prompts if repeatPrompts isn't recognized", function() {
+ // Initialize variables
+ Quizzer.active = true;
+ Quizzer.prompts = [["A1", "A2", "A3", "A4"], ["B1", "B2", "B3", "B4"]];
+ Quizzer.index = 0;
+ Quizzer.repeatPrompts = "test";
+
+ // Run Continue
+ Quizzer.Continue();
+
+ // Assert prompts not changed
+ expect(Quizzer.prompts[0]).to.have.members(["A1", "A2", "A3", "A4"]);
+ expect(Quizzer.prompts[1]).to.have.members(["B1", "B2", "B3", "B4"]);
+ expect(Quizzer.index).to.equal(1);
+ });
+
+ it("Should only change index if repeatPrompts is Immediately", function() {
+ // Initialize variables
+ Quizzer.active = true;
+ Quizzer.prompts = [["A1", "A2", "A3", "A4"], ["B1", "B2", "B3", "B4"]];
+ Quizzer.index = 0;
+ Quizzer.repeatPrompts = "Immediately";
+
+ // Run Continue
+ Quizzer.Continue();
+
+ // Assert prompts not changed
+ expect(Quizzer.prompts[0]).to.have.members(["A1", "A2", "A3", "A4"]);
+ expect(Quizzer.prompts[1]).to.have.members(["B1", "B2", "B3", "B4"]);
+ expect(Quizzer.index).to.equal(0);
+ });
+
+ it("Should only update prompts if repeatPrompts is 5 prompts later", function() {
+ // Initialize variables
+ Quizzer.active = true;
+ Quizzer.prompts = [
+ ["A1", "A2", "A3", "A4"],
+ ["B1", "B2", "B3", "B4"],
+ ["C1", "C2", "C3", "C4"],
+ ["D1", "D2", "D3", "D4"],
+ ["E1", "E2", "E3", "E4"],
+ ["F1", "F2", "F3", "F4"],
+ ["G1", "G2", "G3", "G4"],
+ ];
+ Quizzer.index = 0;
+ Quizzer.repeatPrompts = "5 prompts later";
+
+ // Run Continue
+ Quizzer.Continue();
+
+ // Assert prompts not changed
+ expect(Quizzer.prompts[0]).to.have.members(["B1", "B2", "B3", "B4"]);
+ expect(Quizzer.prompts[1]).to.have.members(["C1", "C2", "C3", "C4"]);
+ expect(Quizzer.prompts[2]).to.have.members(["D1", "D2", "D3", "D4"]);
+ expect(Quizzer.prompts[3]).to.have.members(["E1", "E2", "E3", "E4"]);
+ expect(Quizzer.prompts[4]).to.have.members(["F1", "F2", "F3", "F4"]);
+ expect(Quizzer.prompts[5]).to.have.members(["A1", "A2", "A3", "A4"]);
+ expect(Quizzer.prompts[6]).to.have.members(["G1", "G2", "G3", "G4"]);
+ expect(Quizzer.index).to.equal(0);
+ });
+
+ it("Should only update prompts if repeatPrompts is At the end", function() {
+ // Initialize variables
+ Quizzer.active = true;
+ Quizzer.prompts = [
+ ["A1", "A2", "A3", "A4"],
+ ["B1", "B2", "B3", "B4"],
+ ["C1", "C2", "C3", "C4"],
+ ["D1", "D2", "D3", "D4"],
+ ["E1", "E2", "E3", "E4"],
+ ["F1", "F2", "F3", "F4"],
+ ["G1", "G2", "G3", "G4"],
+ ];
+ Quizzer.index = 0;
+ Quizzer.repeatPrompts = "At the end";
+
+ // Run Continue
+ Quizzer.Continue();
+
+ // Assert prompts not changed
+ expect(Quizzer.prompts[0]).to.have.members(["B1", "B2", "B3", "B4"]);
+ expect(Quizzer.prompts[1]).to.have.members(["C1", "C2", "C3", "C4"]);
+ expect(Quizzer.prompts[2]).to.have.members(["D1", "D2", "D3", "D4"]);
+ expect(Quizzer.prompts[3]).to.have.members(["E1", "E2", "E3", "E4"]);
+ expect(Quizzer.prompts[4]).to.have.members(["F1", "F2", "F3", "F4"]);
+ expect(Quizzer.prompts[5]).to.have.members(["G1", "G2", "G3", "G4"]);
+ expect(Quizzer.prompts[6]).to.have.members(["A1", "A2", "A3", "A4"]);
+ expect(Quizzer.index).to.equal(0);
+ });
+ });
+
+ describe("Enter method", function() {
+ it("Shouldn't do anything if active is false", function() {
+ // Initialize variables
+ Quizzer.prompts = [["A1", "A2", "A3", "A4"], ["B1", "B2", "B3", "B4"]]; // Will change if Continue is called
+ Quizzer.index = 0; // Will be changed if Reset is called
+ Quizzer.repeatPrompts = "At the end";
+
+ // Run Enter
+ Quizzer.Enter();
+
+ // Assert nothing changed
+ expect(Quizzer.prompts[0]).to.have.members(["A1", "A2", "A3", "A4"]);
+ expect(Quizzer.prompts[1]).to.have.members(["B1", "B2", "B3", "B4"]);
+ expect(Quizzer.index).to.equal(0);
+ });
+
+ it("Should call Submit if responceActive is true", function() {
+ // Initialize variables
+ Quizzer.active = true;
+ Quizzer.prompts = [["A1", "A2", "A3", "A4"], ["B1", "B2", "B3", "B4"]]; // Will change if Continue is called
+ Quizzer.index = 0; // Will be changed if Reset is called
+ Quizzer.responce = "A4";
+ Quizzer.repeatPrompts = "At the end";
+ Quizzer.responceActive = true;
+
+ // Run Enter
+ Quizzer.Enter();
+
+ // Assert Submit called
+ expect(Quizzer.prompts[0]).to.have.members(["A1", "A2", "A3", "A4"]);
+ expect(Quizzer.prompts[1]).to.have.members(["B1", "B2", "B3", "B4"]);
+ expect(Quizzer.index).to.equal(1);
+ });
+
+ it("Should call Continue if responceActive is false", function() {
+ // Initialize variables
+ Quizzer.active = true;
+ Quizzer.prompts = [["A1", "A2", "A3", "A4"], ["B1", "B2", "B3", "B4"]]; // Will change if Continue is called
+ Quizzer.index = 0; // Will be changed if Reset is called
+ Quizzer.repeatPrompts = "At the end";
+ Quizzer.responceActive = false;
+
+ // Run Enter
+ Quizzer.Enter();
+
+ // Assert Continue called
+ expect(Quizzer.prompts[0]).to.have.members(["B1", "B2", "B3", "B4"]);
+ expect(Quizzer.prompts[1]).to.have.members(["A1", "A2", "A3", "A4"]);
+ expect(Quizzer.index).to.equal(0);
+ });
+ });
+
+ describe("GetLang method", function () {
+ it("Should return English by default", function() {
+ expect(Quizzer.getLang("")).to.equal("en");
+ expect(Quizzer.getLang("test")).to.equal("en");
+ });
+
+ it("Should return English for English labels", function() {
+ expect(Quizzer.getLang("test english test")).to.equal("en");
+ expect(Quizzer.getLang("ENGLISH")).to.equal("en");
+ });
+
+ it("Should return Spanish for Spanish labels", function() {
+ expect(Quizzer.getLang("test spanish test")).to.equal("es");
+ expect(Quizzer.getLang("SPANISH")).to.equal("es");
+ });
+ });
+
+ describe("Active watch", function() {
+ it("Should update prompts and index", async function() {
+ // Initialize variables
+ Quizzer.startingPrompts = [["A1", "A2", "A3", "A4"], ["B1", "B2", "B3", "B4"]];
+ Quizzer.startingIndex = 1;
+
+ // Assert prompts and index not updated yet
+ expect(Quizzer.prompts.length).to.equal(0);
+ expect(Quizzer.index).to.equal(0);
+
+ // Set active to true
+ Quizzer.active = true;
+ await Quizzer.$nextTick();
+
+ // Assert prompts and index updated
+ expect(Quizzer.prompts).to.have.deep.members([["A1", "A2", "A3", "A4"], ["B1", "B2", "B3", "B4"]]);
+ expect(Quizzer.index).to.equal(1);
+ });
+
+ it("Should call Reset when set to true", async function() {
+ // Initialize variables
+ Quizzer.responce = "test"; // Set to empty string when reset is called
+ expect(Quizzer.responce).to.equal("test");
+
+ // Set active to true
+ Quizzer.active = true;
+ await Quizzer.$nextTick();
+
+ // Assert reset called
+ expect(Quizzer.responce).to.equal("");
+ });
+ });
+
+ describe("Prompt property", function() {
+ it("Should be empty if there aren't any prompt", function() {
+ // Assert prompts and index are correct
+ expect(Quizzer.index).to.equal(0);
+ expect(Quizzer.prompts.length).to.equal(0);
+
+ // Assert prompt is empty
+ expect(Quizzer.prompt.length).to.equal(4);
+ expect(Quizzer.prompt[0]).to.equal("");
+ expect(Quizzer.prompt[1]).to.equal("");
+ expect(Quizzer.prompt[2]).to.equal("");
+ expect(Quizzer.prompt[3]).to.equal("");
+ });
+
+ it("Should be empty if index is invalid", function() {
+ // Initialize index
+ Quizzer.index = 2;
+
+ // Assert prompts is correct
+ expect(Quizzer.prompts.length).to.equal(0);
+
+ // Assert prompt is empty
+ expect(Quizzer.prompt.length).to.equal(4);
+ expect(Quizzer.prompt[0]).to.equal("");
+ expect(Quizzer.prompt[1]).to.equal("");
+ expect(Quizzer.prompt[2]).to.equal("");
+ expect(Quizzer.prompt[3]).to.equal("");
+ });
+
+ it("Should be the current prompt if index is valid", function() {
+ // Initialize prompts and index
+ Quizzer.index = 1;
+ Quizzer.prompts = [
+ ["a1", "b1", "c1", "d1"],
+ ["a2", "b2", "c2", "d2"],
+ ["a3", "b3", "c3", "d3"],
+ ];
+
+ // Assert prompt is correct
+ expect(Quizzer.prompt.length).to.equal(4);
+ expect(Quizzer.prompt[0]).to.equal("a2");
+ expect(Quizzer.prompt[1]).to.equal("b2");
+ expect(Quizzer.prompt[2]).to.equal("c2");
+ expect(Quizzer.prompt[3]).to.equal("d2");
});
});
});
diff --git a/Tests/test.settings.js b/Tests/test.settings.js
@@ -462,4 +462,63 @@ describe("Settings", function() {
}
});
});
+
+ describe("StartSession method", function() {
+ beforeEach(function() {
+ // Initialize Vue
+ loadVue();
+ });
+
+ it("Should throw \"Bad Arguments\" when prompts is null", function() {
+ // Initialize prompts and promptIndex
+ app.prompts = null;
+ app.promptIndex = 0;
+
+ // Assert raises error
+ expect(StartSession).to.throw("Bad arguments.");
+ });
+
+ it("Should throw \"Bad Arguments\" when promptIndex is negative", function() {
+ // Initialize prompts and promptIndex
+ app.prompts = ["a", "b", "c"];
+ app.promptIndex = -1;
+
+ // Assert raises error
+ expect(StartSession).to.throw("Bad arguments.");
+ });
+
+ it("Should throw \"Bad Arguments\" when promptIndex is invalid", function() {
+ // Initialize prompts and promptIndex
+ app.prompts = ["a", "b", "c"];
+ app.promptIndex = 3;
+
+ // Assert raises error
+ expect(StartSession).to.throw("Bad arguments.");
+ });
+
+ it("Should throw \"Terms is empty\" when prompts is empty", function() {
+ // Initialize prompts and promptIndex
+ app.prompts = [];
+ app.promptIndex = 0;
+
+ // Assert raises error
+ expect(StartSession).to.throw("Terms is empty.");
+ });
+ });
+
+ describe("Shuffle method", function() {
+ it("Should not alter list", function() {
+ // Initialize list
+ let list1 = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o"]; // 15 items
+
+ // Shuffle list
+ let list2 = Shuffle(list1);
+
+ // Assert list shuffled
+ expect(list2.length).to.equal(list1.length);
+ for (let item of list2) {
+ expect(list1.includes(item)).to.equal(true);
+ }
+ });
+ });
});
diff --git a/index.html b/index.html
@@ -151,38 +151,15 @@
<button id="settingsResume" onclick="ResumeSession();">Resume</button>
</div>
- <div id="settingsError" class="centered bad"></div>
+ <div id="settingsError" v-show="errorMsg" class="centered bad">{{ errorMsg }}</div>
</div>
</div>
- <div id="quizzer" v-show="state == 'verbQuizzer' || state == 'vocabQuizzer'" hidden>
- <p id="quizzerProgress">{{ promptIndex }} / {{ prompts.length}}</p>
-
- <section>
- <label id="quizzerPromptType" for="quizzerPrompt" :lang="getLang(prompt[0])">{{ prompt[0] }}</label>
- <span id="quizzerPrompt" @click="Read(prompt[1], prompt[0]);">{{ prompt[1] }}</span>
- </section>
-
- <section>
- <label id="quizzerInputType" for="quizzerInput">{{ prompt[2] }}</label>
- <input id="quizzerInput" type="text" v-model="responce" :readonly="!responceActive || inputType == 'Voice'"
- @keyup.ctrl.enter.exact="Reset();" @keyup.enter.exact="Enter();" :lang="getLang(prompt[2])"
- autocomplete="off" spellcheck="false" autocorrect="off" placeholder="Type the answer">
- </section>
-
- <div id="quizzerButtons">
- <button v-if="responceActive" :disabled="inputType == 'Voice'" @click="Submit();">Submit</button>
- <button v-else @click="Continue();">Continue</button>
- <button @click="Reset();">Skip</button>
- </div>
-
- <div id="quizzerFeedback" v-show="!responceActive" class="bad">
- The correct answer is
- <span id="quizzerFeedbackTerm" @click="Read(prompt[3], prompt[2]);">{{ prompt[3].toLowerCase() }}</span>.
- </div>
- <div id="quizzerCongrats" class="good">Congratulations! You made it back to the beginning!</div>
- </div>
+ <quizzer id="quizzer" v-show="state == 'verbQuizzer' || state == 'vocabQuizzer'" :active="state == 'verbQuizzer' || state == 'vocabQuizzer'" hidden
+ :starting-index="promptIndex" :starting-prompts="prompts" @new-prompt="updateProgress"
+ :prompt-type="promptType" :input-type="inputType" :repeat-prompts="repeatPrompts">
+ </quizzer>
</main>