diff --git a/libs/blocks/quiz-entry/quiz-entry.js b/libs/blocks/quiz-entry/quiz-entry.js
index 8559d7f04e..5e569bfe16 100644
--- a/libs/blocks/quiz-entry/quiz-entry.js
+++ b/libs/blocks/quiz-entry/quiz-entry.js
@@ -4,6 +4,12 @@ import { mlField, getMLResults } from './mlField.js';
import { GetQuizOption } from './quizoption.js';
import { quizPopover, getSuggestions } from './quizPopover.js';
+export const locationWrapper = {
+ redirect: (url) => {
+ window.location = url;
+ },
+};
+
const App = ({
quizPath,
maxQuestions,
@@ -222,7 +228,7 @@ const App = ({
if (questionCount.current === maxQuestions || currentQuizState.userFlow.length === 1) {
if (!debug) {
setSelectedQuestion(null);
- window.location = quizPath;
+ locationWrapper.redirect(quizPath);
}
} else {
setSelectedCards({});
diff --git a/libs/blocks/quiz-entry/quizoption.js b/libs/blocks/quiz-entry/quizoption.js
index a2774c89b0..42db01b59b 100644
--- a/libs/blocks/quiz-entry/quizoption.js
+++ b/libs/blocks/quiz-entry/quizoption.js
@@ -89,13 +89,6 @@ export const GetQuizOption = ({
}
};
- useEffect(() => {
- const entry = document.querySelector('.quiz-entry');
- if (entry && entry.querySelector('.no-carousel')) {
- entry.removeChild(entry.querySelector('.no-carousel'));
- }
- }, []);
-
return html`
${index > 0 && html``}
diff --git a/libs/blocks/quiz-entry/utils.js b/libs/blocks/quiz-entry/utils.js
index 0d04f20364..f5551f1fc4 100644
--- a/libs/blocks/quiz-entry/utils.js
+++ b/libs/blocks/quiz-entry/utils.js
@@ -78,7 +78,10 @@ export const handleSelections = (prevSelections, selectedQuestion, selections) =
// de-dup any existing data if they use the ml field and cards.
if (prevSelections.length > 0) {
prevSelections.forEach((selection) => {
- if (selection.selectedQuestion === selectedQuestion) {
+ const jsonSelectionSelectedQustion = JSON.stringify(selection.selectedQuestion);
+ const jsonSelectedQuesion = JSON.stringify(selectedQuestion[0].selectedQuestion);
+ const isSameQuestion = jsonSelectionSelectedQustion === jsonSelectedQuesion;
+ if (isSameQuestion) {
selection.selectedCards = selections;
isNewQuestion = false;
}
diff --git a/test/blocks/quiz-entry/mocks/mock-data.js b/test/blocks/quiz-entry/mocks/mock-data.js
index 01d7a933d6..e317479c83 100644
--- a/test/blocks/quiz-entry/mocks/mock-data.js
+++ b/test/blocks/quiz-entry/mocks/mock-data.js
@@ -100,7 +100,7 @@ const resultsMock = {
options: 'photo',
title: 'Photography',
text: 'Edit or organize my photos',
- icon: '',
+ icon: 'https://milo.adobe.com/drafts/quiz/quiz-ai/search.svg',
image: 'https://main--milo--adobecom.hlx.page/drafts/colloyd/quiz-entry/images/photography.png',
},
{
@@ -664,7 +664,7 @@ const resultsMock = {
},
{
options: 'video',
- next: 'q-rather,q-video',
+ next: 'RESET',
type: 'card',
endpoint: '',
'api-key': '',
diff --git a/test/blocks/quiz-entry/quiz-entry.test.js b/test/blocks/quiz-entry/quiz-entry.test.js
index 993ee21594..d1de93beb2 100644
--- a/test/blocks/quiz-entry/quiz-entry.test.js
+++ b/test/blocks/quiz-entry/quiz-entry.test.js
@@ -3,7 +3,7 @@ import { readFile } from '@web/test-runner-commands';
import { expect } from '@esm-bundle/chai';
import sinon from 'sinon';
import init from '../../../libs/blocks/quiz-entry/quiz-entry.js';
-import { getSuggestions } from '../../../libs/blocks/quiz-entry/quizPopover.js'; // Correct the path as needed
+import { getSuggestions } from '../../../libs/blocks/quiz-entry/quizPopover.js';
let fetchStub;
let quizEntryElement;
@@ -156,4 +156,147 @@ describe('Quiz Entry Component', () => {
await new Promise((resolve) => setTimeout(resolve, 100));
expect(continueButton.classList.contains('disabled')).to.be.false;
});
+ it('should navigate the carousel using keyboard commands', async () => {
+ const options = document.querySelectorAll('.quiz-option');
+ const option = document.querySelector('.quiz-option');
+ option.click();
+ await new Promise((resolve) => setTimeout(resolve, 100));
+ const carousel = document.querySelector('.quiz-options-container');
+ const rightArrowEvent = new KeyboardEvent('keydown', { key: 'ArrowRight' });
+ const leftArrowEvent = new KeyboardEvent('keydown', { key: 'ArrowLeft' });
+ carousel.dispatchEvent(rightArrowEvent);
+ await new Promise((resolve) => setTimeout(resolve, 100));
+ carousel.dispatchEvent(leftArrowEvent);
+ await new Promise((resolve) => setTimeout(resolve, 100));
+ const leftArrow = document.querySelector('.carousel-arrow.arrow-prev');
+ expect(leftArrow).to.not.exist;
+
+ const tabKeyEvent = new KeyboardEvent('keydown', { key: 'Tab' });
+ option.dispatchEvent(tabKeyEvent);
+ await new Promise((resolve) => setTimeout(resolve, 100));
+ expect(option.classList.contains('selected')).to.be.true;
+
+ const spaceKeyEvent = new KeyboardEvent('keydown', { key: ' ', keyCode: 32 });
+ carousel.dispatchEvent(spaceKeyEvent);
+ await new Promise((resolve) => setTimeout(resolve, 100));
+
+ const enterKeyEvent = new KeyboardEvent('keydown', {
+ key: 'Enter',
+ code: 'Enter',
+ keyCode: 13,
+ });
+ carousel.dispatchEvent(enterKeyEvent);
+ await new Promise((resolve) => setTimeout(resolve, 100));
+ expect(options[1].classList.contains('selected')).to.be.false;
+ });
+});
+
+describe('RTL Quiz Entry', () => {
+ beforeEach(async () => {
+ window.lana = { log: sinon.stub() };
+ fetchStub = sinon.stub(window, 'fetch');
+ fetchStub.resolves({
+ ok: true,
+ json: () => Promise.resolve({ suggested_completions: ['designer desk', 'design logos'] }),
+ });
+ document.body.innerHTML = await readFile({ path: './mocks/index.html' });
+ document.documentElement.setAttribute('dir', 'rtl');
+ quizEntryElement = document.querySelector('.quiz-entry');
+ await init(quizEntryElement, quizConfig);
+ await new Promise((resolve) => setTimeout(resolve, 100));
+ });
+
+ afterEach(() => {
+ sinon.restore();
+ });
+
+ it('should navigate the carousel using keyboard commands', async () => {
+ const options = document.querySelectorAll('.quiz-option');
+ const option = document.querySelector('.quiz-option');
+ option.click();
+ await new Promise((resolve) => setTimeout(resolve, 100));
+ const carousel = document.querySelector('.quiz-options-container');
+ const rightArrowEvent = new KeyboardEvent('keydown', { key: 'ArrowRight' });
+ const leftArrowEvent = new KeyboardEvent('keydown', { key: 'ArrowLeft' });
+ carousel.dispatchEvent(rightArrowEvent);
+ await new Promise((resolve) => setTimeout(resolve, 100));
+ carousel.dispatchEvent(leftArrowEvent);
+ await new Promise((resolve) => setTimeout(resolve, 100));
+ const leftArrow = document.querySelector('.carousel-arrow.arrow-prev');
+ expect(leftArrow).to.exist;
+
+ const tabKeyEvent = new KeyboardEvent('keydown', { key: 'Tab' });
+ option.dispatchEvent(tabKeyEvent);
+ await new Promise((resolve) => setTimeout(resolve, 100));
+ expect(option.classList.contains('selected')).to.be.false;
+
+ const spaceKeyEvent = new KeyboardEvent('keydown', { key: ' ', keyCode: 32 });
+ carousel.dispatchEvent(spaceKeyEvent);
+ await new Promise((resolve) => setTimeout(resolve, 100));
+
+ const enterKeyEvent = new KeyboardEvent('keydown', {
+ key: 'Enter',
+ code: 'Enter',
+ keyCode: 13,
+ });
+ carousel.dispatchEvent(enterKeyEvent);
+ await new Promise((resolve) => setTimeout(resolve, 100));
+ expect(options[1].classList.contains('selected')).to.be.false;
+ });
+});
+
+describe('ML Result Trigger', () => {
+ beforeEach(async () => {
+ window.lana = { log: sinon.stub() };
+ fetchStub = sinon.stub(window, 'fetch');
+ const mockApiResponse = {
+ statusCode: 200,
+ data: {
+ data: [
+ {
+ ficode: 'illustrator_cc',
+ prob: '0.33',
+ },
+ {
+ ficode: 'indesign_cc',
+ prob: '0.27',
+ },
+ {
+ ficode: 'free_spark',
+ prob: '0.22',
+ },
+ ],
+ jobName: '',
+ },
+ };
+ fetchStub.resolves({
+ ok: true,
+ json: () => Promise.resolve(mockApiResponse.data),
+ });
+ document.body.innerHTML = await readFile({ path: './mocks/index.html' });
+ quizEntryElement = document.querySelector('.quiz-entry');
+ await init(quizEntryElement, quizConfig);
+ await new Promise((resolve) => setTimeout(resolve, 100));
+ });
+
+ afterEach(() => {
+ sinon.restore();
+ });
+
+ it('Should trigger results fetching scenario', async () => {
+ const mlInputField = document.querySelector('#quiz-input');
+ const testInput = 'design';
+ const inputEvent = new Event('input', { bubbles: true });
+ mlInputField.value = testInput;
+ mlInputField.dispatchEvent(inputEvent);
+ await new Promise((resolve) => setTimeout(resolve, 100));
+
+ const enterKeyEvent = new KeyboardEvent('keypress', {
+ key: 'Enter',
+ code: 'Enter',
+ keyCode: 13,
+ });
+ mlInputField.dispatchEvent(enterKeyEvent);
+ expect(mlInputField.value).to.equal('design');
+ });
});
diff --git a/test/blocks/quiz-entry/utils.test.js b/test/blocks/quiz-entry/utils.test.js
new file mode 100644
index 0000000000..700f349907
--- /dev/null
+++ b/test/blocks/quiz-entry/utils.test.js
@@ -0,0 +1,149 @@
+/* eslint-disable no-promise-executor-return */
+import { readFile } from '@web/test-runner-commands';
+import { expect } from '@esm-bundle/chai';
+import sinon from 'sinon';
+import { handleNext, getQuizJson, handleSelections, getQuizEntryData } from '../../../libs/blocks/quiz-entry/utils.js'; // Correct the path as needed
+
+let fetchStub;
+const { default: mockData } = await import('./mocks/mock-data.js');
+const mockQuestionsData = mockData.questions;
+const mockStringsData = mockData.strings;
+const quizConfig = {
+ quizPath: '/drafts/quiz/',
+ maxQuestions: 1,
+ analyticsQuiz: 'uarv4',
+ analyticsType: 'cc:app-reco',
+ questionData: undefined,
+ stringsData: undefined,
+};
+const selectedQuestion = {
+ questions: 'q-category',
+ 'max-selections': '3',
+ 'min-selections': '1',
+};
+const userInputSelections = { photo: true };
+const userInputSelectionsNot = { '3d': true };
+const userInputSelectionsReset = { video: true };
+
+const userFlow = [];
+const nextFlow = { nextFlow: ['q-rather', 'q-photo'] };
+const nextFlowNot = { nextFlow: ['q-3d'] };
+const nextFlowReset = { nextFlow: [] };
+const prevSelections = [];
+const selections = ['photo'];
+const nextSelectionsExpected = {
+ nextSelections: [
+ {
+ selectedCards: [
+ 'photo',
+ ],
+ selectedQuestion: {
+ 'max-selections': '3',
+ 'min-selections': '1',
+ questions: 'q-category',
+ },
+ },
+ ],
+};
+
+describe('Quiz Entry Utils', () => {
+ beforeEach(async () => {
+ window.lana = { log: sinon.stub() };
+ fetchStub = sinon.stub(window, 'fetch');
+ fetchStub.resolves({
+ ok: true,
+ json: () => Promise.resolve(mockData),
+ });
+ });
+
+ afterEach(() => {
+ sinon.restore();
+ });
+
+ it('should handle the next flow of questions', async () => {
+ const nextQuestion = handleNext(
+ mockQuestionsData,
+ selectedQuestion,
+ userInputSelections,
+ userFlow,
+ );
+ expect(nextQuestion).to.deep.equal(nextFlow);
+ });
+
+ it('should handle the next flow of questions with not()', async () => {
+ const nextQuestion = handleNext(
+ mockQuestionsData,
+ selectedQuestion,
+ userInputSelectionsNot,
+ userFlow,
+ );
+ expect(nextQuestion).to.deep.equal(nextFlowNot);
+ });
+
+ it('should handle the next flow of questions with reset()', async () => {
+ const nextQuestion = handleNext(
+ mockQuestionsData,
+ selectedQuestion,
+ userInputSelectionsReset,
+ userFlow,
+ );
+ expect(nextQuestion).to.deep.equal(nextFlowReset);
+ });
+
+ it('should fetch quiz data', async () => {
+ const [questions, strings] = await getQuizJson('./mocks/');
+ expect(questions.questions).to.deep.equal(mockQuestionsData);
+ expect(strings.strings).to.deep.equal(mockStringsData);
+ });
+});
+
+describe('Quiz Entry Utils failed request', () => {
+ beforeEach(async () => {
+ window.lana = { log: sinon.stub() };
+ fetchStub = sinon.stub(window, 'fetch');
+ fetchStub.resolves({ ok: false });
+ });
+
+ afterEach(() => {
+ sinon.restore();
+ });
+
+ it('should log an error when fetching quiz data fails', async () => {
+ await getQuizJson('./mocks/');
+ expect(window.lana.log.called).to.be.true;
+ });
+ it('should return nextSelections on handleSelections', async () => {
+ const nextSelections = handleSelections(prevSelections, selectedQuestion, selections);
+ expect(nextSelections).to.deep.equal(nextSelectionsExpected);
+ });
+
+ it('should de-dup any existing data if they use the ml field and cards.', async () => {
+ const prevSelectionsLength = [{
+ selectedQuestion: {
+ 'max-selections': '3',
+ 'min-selections': '1',
+ questions: 'q-category',
+ },
+ }];
+
+ const selectedQuestionPrev = [{
+ selectedQuestion: {
+ 'max-selections': '3',
+ 'min-selections': '1',
+ questions: 'q-category',
+ },
+ }];
+
+ const nextSelections = handleSelections(prevSelectionsLength, selectedQuestionPrev, selections);
+ await new Promise((resolve) => setTimeout(resolve, 100));
+ expect(nextSelections).to.deep.equal(nextSelections);
+ });
+
+ it('should return quizPath, maxQuestions, analyticsQuiz, analyticsType, questionData', async () => {
+ document.body.innerHTML = await readFile({ path: './mocks/index.html' });
+ const el = document.querySelector('.quiz-entry');
+
+ const quizEntryData = await getQuizEntryData(el);
+ expect(quizEntryData).to.deep.equal(quizConfig);
+ });
+});