From 42eda8f1cc2269ab5f9c684a5df9641dac0f17b7 Mon Sep 17 00:00:00 2001 From: Nikita Ovchinnikov Date: Fri, 13 Aug 2021 18:05:59 +0300 Subject: [PATCH 1/2] Test refactoring --- .../GameWithHooks/GameWithHooks.test.tsx | 144 ++--- .../__snapshots__/GameWithHooks.test.tsx.snap | 587 ++---------------- src/modules/GameWithHooks/useGame.test.ts | 15 +- src/modules/GameWithHooks/useGame.ts | 2 + .../useGameCheckGameField.test.ts | 32 + stryker.conf.js | 1 + 6 files changed, 130 insertions(+), 651 deletions(-) create mode 100644 src/modules/GameWithHooks/useGameCheckGameField.test.ts diff --git a/src/modules/GameWithHooks/GameWithHooks.test.tsx b/src/modules/GameWithHooks/GameWithHooks.test.tsx index c59985c..1e8c937 100644 --- a/src/modules/GameWithHooks/GameWithHooks.test.tsx +++ b/src/modules/GameWithHooks/GameWithHooks.test.tsx @@ -2,112 +2,56 @@ import React from 'react'; import userEvent from '@testing-library/user-event'; import { render, screen } from '@testing-library/react'; -import { CellState } from '@/helpers/Field'; - import { GameWithHooks } from './GameWithHooks'; -const { empty: e, hidden: h, bomb: b, flag: f } = CellState; - -jest.mock('@/helpers/Field'); +const mockOnClick = jest.fn(); +const mockOnChangeLevel = jest.fn(); +const mockOnReset = jest.fn(); + +jest.mock('./useGame', () => ({ + __esModule: true, + useGame: () => ({ + level: 'beginner', + isGameOver: true, + isWin: false, + settings: [9, 10], + playerField: [ + [10, 10], + [10, 10], + ], + onClick: mockOnClick, + onChangeLevel: mockOnChangeLevel, + onReset: mockOnReset, + }), +})); + +beforeEach(() => { + jest.clearAllMocks(); +}); describe('GameWithHooks test cases', () => { - describe('Render behaviour', () => { - it('Render game field by default', () => { - const { asFragment } = render(); - expect(screen.getAllByRole('cell')).toHaveLength(81); - expect(asFragment()).toMatchSnapshot(); - }); - it('onChange game level handler', () => { - render(); - expect(screen.getAllByRole('cell')).toHaveLength(81); - userEvent.selectOptions(screen.getByRole('combobox'), 'intermediate'); - expect(screen.getAllByRole('cell')).toHaveLength(256); - userEvent.selectOptions(screen.getByRole('combobox'), 'expert'); - expect(screen.getAllByRole('cell')).toHaveLength(484); - }); + it('Render game field by default', () => { + const { asFragment } = render(); + expect(asFragment()).toMatchSnapshot(); }); - describe('Open cell test cases', () => { - it('Open empty cell on the beginner level', () => { - render(); - - expect(screen.queryAllByRole('cell', { name: String(e) })).toHaveLength( - 0 - ); - - userEvent.click(screen.getByTestId('0,0')); - - expect(screen.getAllByRole('cell', { name: String(e) })).toHaveLength(18); - }); - it('Click to the non-empty cells area', () => { - render(); - - userEvent.click(screen.getByTestId('0,8')); - - expect(screen.getAllByRole('cell', { name: String(1) })).toHaveLength(1); - }); + it('Cell click works fine', () => { + render(); + userEvent.click(screen.getByTestId('0,0')); + expect(mockOnClick).toHaveBeenCalled(); }); - describe('OnClick with OnChangeGameLevel and OnReset', () => { - it('Check click to the cell when the level is changed', () => { - render(); - expect(screen.getAllByRole('cell')).toHaveLength(81); - - userEvent.selectOptions(screen.getByRole('combobox'), 'intermediate'); - expect(screen.getAllByRole('cell')).toHaveLength(256); - - userEvent.click(screen.getByTestId('15,15')); - expect(screen.getAllByRole('cell', { name: String(e) })).toHaveLength(2); - - userEvent.selectOptions(screen.getByRole('combobox'), 'expert'); - expect(screen.getAllByRole('cell')).toHaveLength(484); - - userEvent.click(screen.getByTestId('21,21')); - expect(screen.getAllByRole('cell', { name: String(e) })).toHaveLength(1); - expect(screen.getAllByRole('cell', { name: String(1) })).toHaveLength(2); - expect(screen.getAllByRole('cell', { name: String(2) })).toHaveLength(1); - }); - it('onReset game handler', () => { - render(); - expect(screen.getAllByRole('cell', { name: String(h) })).toHaveLength(81); - - userEvent.click(screen.getByTestId('0,8')); - expect(screen.getAllByRole('cell', { name: String(1) })).toHaveLength(1); - - userEvent.click(screen.getByTestId('0,0')); - expect(screen.getAllByRole('cell', { name: String(e) })).toHaveLength(18); - - userEvent.click(screen.getByRole('button')); - expect(screen.getAllByRole('cell', { name: String(h) })).toHaveLength(81); - }); + it('Reset handler works fine', () => { + render(); + userEvent.click(screen.getByRole('button')); + expect(mockOnReset).toHaveBeenCalled(); }); - describe('Game over behavior', () => { - it('Player loose the game', () => { - render(); - - userEvent.click(screen.getByTestId('0,8')); - expect(screen.getAllByRole('cell', { name: String(1) })).toHaveLength(1); - - userEvent.click(screen.getByTestId('0,0')); - expect(screen.getAllByRole('cell', { name: String(e) })).toHaveLength(18); - - userEvent.click(screen.getByTestId('0,7')); - - const gameLoosePopup = screen.getByText('🙁'); - - expect(gameLoosePopup).toBeInTheDocument(); - - expect(screen.queryAllByRole('cell', { name: String(h) })).toHaveLength( - 0 - ); - expect(screen.getAllByRole('cell', { name: String(e) })).toHaveLength(27); - expect(screen.getAllByRole('cell', { name: String(1) })).toHaveLength(30); - expect(screen.getAllByRole('cell', { name: String(2) })).toHaveLength(12); - expect(screen.getAllByRole('cell', { name: String(3) })).toHaveLength(2); - - userEvent.click(gameLoosePopup); - - expect(screen.getAllByRole('cell', { name: String(h) })).toHaveLength(81); - - expect(screen.queryByText('🙁')).not.toBeInTheDocument(); - }); + it('Change level works fine', () => { + render(); + userEvent.selectOptions(screen.getByRole('combobox'), 'intermediate'); + expect(mockOnChangeLevel).toHaveBeenCalled(); + }); + it('Game over reset the game state', () => { + render(); + userEvent.click(screen.getByText('🙁')); + expect(mockOnReset).toHaveBeenCalled(); }); }); diff --git a/src/modules/GameWithHooks/__snapshots__/GameWithHooks.test.tsx.snap b/src/modules/GameWithHooks/__snapshots__/GameWithHooks.test.tsx.snap index 40d722c..f2fbb86 100644 --- a/src/modules/GameWithHooks/__snapshots__/GameWithHooks.test.tsx.snap +++ b/src/modules/GameWithHooks/__snapshots__/GameWithHooks.test.tsx.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`GameWithHooks test cases Render behaviour Render game field by default 1`] = ` +exports[`GameWithHooks test cases Render game field by default 1`] = ` .emotion-0 { position: relative; @@ -116,15 +116,40 @@ exports[`GameWithHooks test cases Render behaviour Render game field by default } .emotion-15 { + top: 60%; + left: 50%; + z-index: 11; + width: 8vw; + height: 8vw; + font-size: 4vw; + text-align: center; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + cursor: pointer; + line-height: 8vw; + position: absolute; + border-radius: 50%; + pointer-events: auto; + background-color: #d1d1d1; + -webkit-transform: translate(-50%, -50%); + -moz-transform: translate(-50%, -50%); + -ms-transform: translate(-50%, -50%); + transform: translate(-50%, -50%); + border: 1px solid rgba(51, 51, 51, 0.25); +} + +.emotion-16 { display: grid; - grid-template-columns: repeat(9, auto); + grid-template-columns: repeat(2, auto); width: -webkit-max-content; width: -moz-max-content; width: max-content; padding: 1vw; } -.emotion-16 { +.emotion-17 { display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; @@ -150,7 +175,7 @@ exports[`GameWithHooks test cases Render behaviour Render game field by default border-color: white #9e9e9e #9e9e9e white; } -.emotion-16:hover { +.emotion-17:hover { -webkit-filter: brightness(1.1); filter: brightness(1.1); } @@ -231,576 +256,42 @@ exports[`GameWithHooks test cases Render behaviour Render game field by default
+ 🙁 +
+
10
10
- 10 -
-
- 10 -
-
- 10 -
-
- 10 -
-
- 10 -
-
- 10 -
-
- 10 -
-
10
10
-
- 10 -
-
- 10 -
-
- 10 -
-
- 10 -
-
- 10 -
-
- 10 -
-
- 10 -
-
- 10 -
-
- 10 -
-
- 10 -
-
- 10 -
-
- 10 -
-
- 10 -
-
- 10 -
-
- 10 -
-
- 10 -
-
- 10 -
-
- 10 -
-
- 10 -
-
- 10 -
-
- 10 -
-
- 10 -
-
- 10 -
-
- 10 -
-
- 10 -
-
- 10 -
-
- 10 -
-
- 10 -
-
- 10 -
-
- 10 -
-
- 10 -
-
- 10 -
-
- 10 -
-
- 10 -
-
- 10 -
-
- 10 -
-
- 10 -
-
- 10 -
-
- 10 -
-
- 10 -
-
- 10 -
-
- 10 -
-
- 10 -
-
- 10 -
-
- 10 -
-
- 10 -
-
- 10 -
-
- 10 -
-
- 10 -
-
- 10 -
-
- 10 -
-
- 10 -
-
- 10 -
-
- 10 -
-
- 10 -
-
- 10 -
-
- 10 -
-
- 10 -
-
- 10 -
-
- 10 -
-
- 10 -
-
- 10 -
-
- 10 -
-
- 10 -
-
- 10 -
-
- 10 -
-
- 10 -
-
- 10 -
-
- 10 -
-
- 10 -
diff --git a/src/modules/GameWithHooks/useGame.test.ts b/src/modules/GameWithHooks/useGame.test.ts index 36288ca..b545443 100644 --- a/src/modules/GameWithHooks/useGame.test.ts +++ b/src/modules/GameWithHooks/useGame.test.ts @@ -18,7 +18,7 @@ describe('useGame test cases', () => { describe('Render behaviour', () => { it('Render hook by default', () => { const { result } = renderHook(useGame); - const { level, isGameOver, isWin, settings, playerField } = + const { level, isGameOver, isWin, settings, playerField, gameField } = result.current; expect({ level, isGameOver, isWin, settings }).toStrictEqual({ @@ -28,6 +28,7 @@ describe('useGame test cases', () => { settings: GameSettings.beginner, }); expect(playerField).toHaveLength(9); + expect(flatWithFilter(gameField, b)).toHaveLength(10); }); it('onChange game level handler', () => { const { result } = renderHook(useGame); @@ -78,7 +79,7 @@ describe('useGame test cases', () => { expect(flatWithFilter(newPlayerField, 1)).toHaveLength(1); }); }); - describe('OnClick with OnChangeGameLevel and OnReset', () => { + describe('OnClick with OnChangeGameLevel', () => { it('Check click to the cell when the level is changed', () => { const { result } = renderHook(useGame); const { playerField, onChangeLevel } = result.current; @@ -125,9 +126,17 @@ describe('useGame test cases', () => { expect(flatWithFilter(newPlayerField, e)).toHaveLength(18); act(onReset); - const { playerField: finalPlayerField } = result.current; + const { + playerField: finalPlayerField, + isWin, + isGameOver, + gameField, + } = result.current; + expect(isWin).toBe(false); + expect(isGameOver).toBe(false); expect(flatWithFilter(finalPlayerField, h)).toHaveLength(81); + expect(flatWithFilter(gameField, b)).toHaveLength(10); }); }); describe('Game over behavior', () => { diff --git a/src/modules/GameWithHooks/useGame.ts b/src/modules/GameWithHooks/useGame.ts index 122431e..125759c 100644 --- a/src/modules/GameWithHooks/useGame.ts +++ b/src/modules/GameWithHooks/useGame.ts @@ -17,6 +17,7 @@ interface ReturnType { isWin: boolean; settings: [number, number]; playerField: Field; + gameField: Field; onClick: (coords: Coords) => void; onChangeLevel: (level: LevelNames) => void; onReset: () => void; @@ -75,6 +76,7 @@ export const useGame = (): ReturnType => { isWin, settings: [size, bombs], playerField, + gameField, onClick, onChangeLevel, onReset, diff --git a/src/modules/GameWithHooks/useGameCheckGameField.test.ts b/src/modules/GameWithHooks/useGameCheckGameField.test.ts new file mode 100644 index 0000000..6d1e4e5 --- /dev/null +++ b/src/modules/GameWithHooks/useGameCheckGameField.test.ts @@ -0,0 +1,32 @@ +import { renderHook, act } from '@testing-library/react-hooks'; + +import { CellState, Field } from '@/helpers/Field'; + +import { useGame } from './useGame'; + +const { bomb: b, hidden: h } = CellState; + +const flatWithFilter = (field: Field, cond: number) => + field.flat().filter((v) => v === cond); + +describe('useGame test cases', () => { + it('Render hook by default', () => { + const { result } = renderHook(useGame); + const { gameField } = result.current; + + expect(flatWithFilter(gameField, b)).toHaveLength(10); + }); + it('onReset game handler', () => { + const { result } = renderHook(useGame); + const { playerField, onClick, onReset } = result.current; + + expect(playerField).toHaveLength(9); + + act(() => onClick([5, 5])); + act(onReset); + + const { gameField } = result.current; + + expect(flatWithFilter(gameField, b)).toHaveLength(10); + }); +}); diff --git a/stryker.conf.js b/stryker.conf.js index 4d0ecf8..3811d5f 100644 --- a/stryker.conf.js +++ b/stryker.conf.js @@ -10,5 +10,6 @@ module.exports = { mutate: [ 'src/**/*.ts?(x)', '!src/**/*@(.test|.spec|Spec|stories|styled).ts?(x)', + '!src/**/__mocks__/**/*', ], }; From 62dbddc2188400500cb33d5f076a2057d1c7c084 Mon Sep 17 00:00:00 2001 From: Nikita Ovchinnikov Date: Fri, 13 Aug 2021 18:08:45 +0300 Subject: [PATCH 2/2] Test refactoring --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index b116076..c102d3e 100644 --- a/README.md +++ b/README.md @@ -187,3 +187,7 @@ ### Game custom hook [Pull request](https://github.com/nickovchinnikov/minesweeper/pull/30) + +### Test refactoring + +[Pull request](https://github.com/nickovchinnikov/minesweeper/pull/31/files)