Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: allow to retake errors #8

Merged
merged 1 commit into from
Nov 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 30 additions & 5 deletions src/App.test.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,38 @@
import React from 'react';
import {fireEvent, render, screen} from '@testing-library/react';
import App from './App';
import {AnsweredQuestion, GameState, Question} from "./providers/MultiplicationTableStateProvider";

function playGame(selectedNumber: string, container: HTMLElement) {
function playGame(selectedNumber: string, container: HTMLElement, failOne = false) {
let questionLabel = container.querySelector('.question');
let index = 1;
do {
const questionText = questionLabel?.textContent || '';
const [firstNumber, secondNumber] = questionText.split(' * ');
expect(firstNumber).toEqual(selectedNumber);
expect(parseInt(secondNumber)).toBeGreaterThanOrEqual(1);
expect(parseInt(secondNumber)).toBeLessThan(11);
const answer = parseInt(firstNumber) * parseInt(secondNumber);
let answer = parseInt(firstNumber) * parseInt(secondNumber);
if(failOne && index === 1) {
answer = answer - 1;
}
let input = container.querySelector('#answer') as HTMLInputElement;
fireEvent.change(input, {target: {value: answer.toString()}});
fireEvent.submit(input!);
input = container.querySelector('#answer') as HTMLInputElement;
// after last answer the input is removed
expect(input ? input.value : '').toBe('');
questionLabel = container.querySelector('.question');
index++;
} while (questionLabel);
}

function selectNumber(selectedNumber: string, container: HTMLElement) {
let number = screen.getByText(selectedNumber);
fireEvent.click(number);
fireEvent.click(container.querySelector('#startGame')!);
}

describe('Multiplcation Table Game', () => {
test('renders initial state', () => {
const {container} = render(<App/>);
Expand All @@ -35,9 +47,22 @@ describe('Multiplcation Table Game', () => {
test('plays game', () => {
const {container} = render(<App/>);
const selectedNumber = '2';
let number = screen.getByText(selectedNumber);
fireEvent.click(number);
fireEvent.click(container.querySelector('#startGame')!);
selectNumber(selectedNumber, container);
playGame(selectedNumber, container);
});

test('after retaking wrong answers', () => {
const {container} = render(<App/>);
const selectedNumber = '2';
selectNumber(selectedNumber, container);

playGame(selectedNumber, container, true);

const retakeButton = container.querySelector('#retake') as HTMLButtonElement;
expect(retakeButton).toBeInTheDocument();
fireEvent.click(retakeButton!);
//
// let input = container.querySelector('input');
// expect(input).toBeInTheDocument();
});
});
17 changes: 9 additions & 8 deletions src/components/actionsBar/actionsBar.css
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@
margin-top: 1em;
gap: 2em;

.startGame {
background-color: #4CAF50;
button {
color: #000000;
font-weight: bold;
padding: 1em 2em;
Expand All @@ -15,14 +14,16 @@
cursor: pointer;
}

.startGame {
background-color: #4CAF50;
}

.restartGame {
background-color: #ff7075;
color: #000000;
font-weight: bold;
padding: 1em 2em;
border: none;
border-radius: 0.6em;
cursor: pointer;
}

.retake {
background-color: #70aeff;
}

.disabled {
Expand Down
27 changes: 21 additions & 6 deletions src/components/actionsBar/actionsBar.test.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
import {MultiplicationTableStateProvider} from "../../providers/MultiplicationTableStateProvider";
import {
AnsweredQuestion,
GameState,
MultiplicationTableStateProvider, Question
} from "../../providers/MultiplicationTableStateProvider";
import {fireEvent, render} from "@testing-library/react";
import ActionsBar from "./index";

describe('NumbersSelector component', () => {
function getUi(selectedNumbers: number[]) {
function getUi(selectedNumbers: number[], gameState: GameState = GameState.NotStarted, answers: AnsweredQuestion[] = []) {
return (
<MultiplicationTableStateProvider selectedNumbers={selectedNumbers}>
<MultiplicationTableStateProvider
selectedNumbers={selectedNumbers}
gameState={gameState}
answeredQuestions={answers}>
<ActionsBar/>
</MultiplicationTableStateProvider>
);
Expand All @@ -21,18 +28,20 @@ describe('NumbersSelector component', () => {
expect(restartButton).toBeInTheDocument();
expect(restartButton).toBeDisabled();
expect(restartButton).toHaveClass('disabled');
const retakeButton = container.querySelector('#retake');
expect(retakeButton).not.toBeInTheDocument();
});

test('renders enabled start button', () => {
const {container} = render(getUi([1,2]));
const {container} = render(getUi([1, 2]));
const button = container.querySelector('#startGame');
expect(button).toBeInTheDocument();
expect(button).not.toBeDisabled();
expect(button).not.toHaveClass('disabled');
});

test('after clicking start start is disabled and restart enabled', () => {
const {container} = render(getUi([1,2]));
const {container} = render(getUi([1, 2]));
const startButton = container.querySelector('#startGame');
fireEvent.click(startButton!);
expect(startButton).toBeDisabled();
Expand All @@ -42,7 +51,7 @@ describe('NumbersSelector component', () => {
});

test('after clicking restart restart is disabled and start enabled', () => {
const {container} = render(getUi([1,2]));
const {container} = render(getUi([1, 2]));
const startButton = container.querySelector('#startGame');
const restartButton = container.querySelector('#restartGame');
fireEvent.click(startButton!);
Expand All @@ -52,4 +61,10 @@ describe('NumbersSelector component', () => {
expect(restartButton).toBeDisabled();
expect(restartButton).toHaveClass('disabled');
});

test('when game finished and are wrong answers retake button is shown', () => {
const {container} = render(getUi([1, 2], GameState.Finished, [new AnsweredQuestion(new Question(1, 2), 3)]));
const retakeButton = container.querySelector('#retake');
expect(retakeButton).toBeInTheDocument();
});
});
9 changes: 9 additions & 0 deletions src/components/actionsBar/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ export default function ActionsBar() {
dispatch({type: 'restart', payload: 0});
}

const wrongAnswers = context.gameState === GameState.Finished ? context.answeredQuestions.filter(q => !q.isCorrect) : [];

return (
<div className={'actionsBar'}>
<button id={'startGame'}
Expand All @@ -33,6 +35,13 @@ export default function ActionsBar() {
onClick={handleRestart}>
Restart
</button>
{wrongAnswers.length > 0 &&
<button id={'retake'}
className={'retake'}
onClick={() => dispatch({type: 'retake', payload: 0})}>
Powtórz błędne
</button>
}
</div>
);
}
6 changes: 3 additions & 3 deletions src/components/answeredQuestions/answeredQuestions.css
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
}

.answersTable thead {
background-color: #f2f2f2;
background-color: #70aeff;
font-size: 1em;
font-weight: bold;
}
Expand All @@ -25,9 +25,9 @@
}

.correctAnswer {
background-color: rgba(0, 255, 0, 0.51);
background-color: #47FA4782;
}

.wrongAnswer {
background-color: rgb(255, 112, 117);
background-color: #ff7075;
}
17 changes: 12 additions & 5 deletions src/components/multiplicationGame/multiplicationGame.test.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
import React from 'react';
import {fireEvent, render} from "@testing-library/react";
import {GameState, MultiplicationTableStateProvider, Question} from "../../providers/MultiplicationTableStateProvider";
import {
AnsweredQuestion,
GameState,
MultiplicationTableStateProvider,
Question
} from "../../providers/MultiplicationTableStateProvider";
import MultiplicationGame from "./index";

describe('MultiplicationGame component', () => {
function renderGame(gameStarted = GameState.NotStarted, questions = [] as Question[], currentQuestionIndex = 0) {
function renderGame(state = GameState.NotStarted, questions = [] as Question[], answers: AnsweredQuestion[] = []) {
const {container} = render(
<MultiplicationTableStateProvider
gameState={gameStarted}
gameState={state}
questions={questions}
currentQuestionIndex={currentQuestionIndex}>
currentQuestionIndex={0}
answeredQuestions={answers}>
<MultiplicationGame/>
</MultiplicationTableStateProvider>
);
Expand Down Expand Up @@ -47,7 +53,8 @@ describe('MultiplicationGame component', () => {
let gameContainer = container.querySelector('.multiplicationTable');
expect(gameContainer).toBeInTheDocument();

const input: HTMLInputElement | null = container.querySelector('input');
const input = container.querySelector('input');
expect(input).toBeInTheDocument();
fireEvent.change(input!, {target: {value: '6'}});
fireEvent.submit(input!);

Expand Down
2 changes: 1 addition & 1 deletion src/components/numbersSelector/numbersSelector.css
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
}

.selected {
background-color: #61dafb;
background-color: #70aeff;
}

#clearButton{
Expand Down
10 changes: 10 additions & 0 deletions src/providers/MultiplicationTableStateProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,14 @@ function questionWasAnswered(state: MultiplicationTableContextType, payload: num
return {...state, gameState, questions, answeredQuestions};
}

function questionsToRetake(answeredQuestions: AnsweredQuestion[]) {
return answeredQuestions.filter(q => !q.isCorrect).map(q => q.question);
}

function correctAnswers(answeredQuestions: AnsweredQuestion[]) {
return answeredQuestions.filter(q => q.isCorrect);
}

function selectedNumbersReducer(state: MultiplicationTableContextType, action: { type: string, payload: number }) {
switch (action.type) {
case 'add':
Expand All @@ -158,6 +166,8 @@ function selectedNumbersReducer(state: MultiplicationTableContextType, action: {
return {...state, gameState: GameState.NotStarted, selectedNumbers: [], answeredQuestions: []};
case 'answer':
return questionWasAnswered(state, action.payload);
case 'retake':
return {...state, gameState: GameState.InProgress, questions: questionsToRetake(state.answeredQuestions), currentQuestionIndex: 0, answeredQuestions: correctAnswers(state.answeredQuestions)};
default: {
throw Error('Unknown action: ' + action.type);
}
Expand Down
Loading