Skip to content

Commit

Permalink
E2E tests - Cypress (#346)
Browse files Browse the repository at this point in the history
* Get rid of blobs

* Rename test script to test-unit

* Install Cypress

* Add repro for #2

* Install cypress-real-events

* Add repro for #3

* Add repro for #112

* Clean up

* Add repro for #129

* Fix unit tests

* Add app test

* Install eslint-plugin-cypress
- Lint e2e tests
- Remove babel-eslint

* Install eslint-plugin-mocha

* Add a large full app test

* Wait for network requests

* Bring back test script

* Move custom actions and selectors to lib/

* Add cypress GH action

* Update badges

* Add CI env variable

* Fix condition

* Rename actions

* Use baseUrl

* Fix action

* Prefix env variable
- @see https://github.com/cypress-io/github-action?tab=readme-ov-file#env

* Fix base url

* Add Cypress to README

* Fix npm run test

* Remove npm-run-all
  • Loading branch information
kamilmielnik authored Jul 14, 2024
1 parent 39aeb23 commit e7f7138
Show file tree
Hide file tree
Showing 42 changed files with 2,005 additions and 321 deletions.
17 changes: 13 additions & 4 deletions .eslintrc.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
module.exports = {
parser: 'babel-eslint',

extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended', 'prettier'],
extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended', 'prettier', 'plugin:cypress/recommended'],

parserOptions: {
ecmaVersion: 6,
Expand All @@ -20,13 +18,14 @@ module.exports = {
jquery: true,
},

plugins: ['@typescript-eslint', 'react', 'react-hooks', 'import'],
plugins: ['@typescript-eslint', 'react', 'react-hooks', 'import', 'cypress', 'mocha'],

globals: {
RequestInfo: true,
RequestInit: true,
ServiceWorkerGlobalScope: true,
beforeAll: true,
cy: true,
define: true,
describe: true,
expect: true,
Expand Down Expand Up @@ -59,6 +58,13 @@ module.exports = {
'@typescript-eslint/no-non-null-assertion': 'off',
},
},

{
files: ['*.spec.cy.ts'],
rules: {
'max-statements': 'off',
},
},
],

settings: {
Expand Down Expand Up @@ -543,5 +549,8 @@ module.exports = {
],
'react/jsx-uses-react': 'error',
'react/jsx-uses-vars': 'error',

'mocha/no-exclusive-tests': 'error',
'mocha/no-skipped-tests': 'error',
},
};
24 changes: 24 additions & 0 deletions .github/workflows/cypress.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
name: Tests - Cypress

on:
push:
branches: [master]
pull_request:
branches: [master]

jobs:
cypress-run:
runs-on: ubuntu-latest

steps:
- name: Checkout
uses: actions/checkout@v4
# Install npm dependencies, cache them correctly
# and run all Cypress tests
- name: Cypress run
uses: cypress-io/github-action@v6
with:
build: npm run build
start: npm start
env:
CYPRESS_BASE_URL: http://localhost:3333
19 changes: 9 additions & 10 deletions .github/workflows/eslint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,19 @@ on:

jobs:
build:

runs-on: ubuntu-latest

strategy:
matrix:
node-version: [20.x]

steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- run: npm ci
- run: npm run lint
env:
CI: true
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- run: npm ci
- run: npm run lint
env:
CI: true
27 changes: 27 additions & 0 deletions .github/workflows/jest.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: Tests - Jest

on:
push:
branches: [master]
pull_request:
branches: [master]

jobs:
build:
runs-on: ubuntu-latest

strategy:
matrix:
node-version: [20.x]

steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- run: npm ci
- run: npm run build
- run: npm run test-jest
env:
CI: true
28 changes: 0 additions & 28 deletions .github/workflows/test.yml

This file was deleted.

8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,10 @@
</p>

<p>
<img src="https://github.com/kamilmielnik/scrabble-solver/workflows/Build/badge.svg" alt="Build" />
<img src="https://github.com/kamilmielnik/scrabble-solver/workflows/Test/badge.svg" alt="Test" />
<img src="https://github.com/kamilmielnik/scrabble-solver/workflows/ESLint/badge.svg" alt="ESLint" />
<img src="https://github.com/kamilmielnik/scrabble-solver/workflows/build.yml/badge.svg" alt="Build" />
<img src="https://github.com/kamilmielnik/scrabble-solver/workflows/jest.yml/badge.svg" alt="Jest Tests" />
<img src="https://github.com/kamilmielnik/scrabble-solver/workflows/cypress.yml/badge.svg" alt="Cypress Tests" />
<img src="https://github.com/kamilmielnik/scrabble-solver/workflows/eslint.yml/badge.svg" alt="ESLint" />
</p>

<img alt="Screencast GIF showing user interface when solving for oxyphenbutazone, which is a top-scoring word in English version of Scrabble" src="https://raw.githubusercontent.com/kamilmielnik/scrabble-solver/master/screencast.gif" />
Expand Down Expand Up @@ -183,6 +184,7 @@ npm run build -w @scrabble-solver/word-lists
- [CSS Modules](https://github.com/css-modules/css-modules)
- [include-media](https://eduardoboucas.github.io/include-media/)
- [Lerna](https://lerna.js.org/)
- [Cypress](https://www.cypress.io/)
- [Jest](https://jestjs.io/)
- [ESLint](https://eslint.org/)
- [Prettier](https://prettier.io/)
Expand Down
9 changes: 9 additions & 0 deletions cypress.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { defineConfig } from 'cypress';

export default defineConfig({
e2e: {
baseUrl: 'http://localhost:3000',
viewportHeight: 900,
viewportWidth: 1440,
},
});
106 changes: 106 additions & 0 deletions cypress/e2e/app.spec.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import {
assertResult,
getBoardTile,
getDictionary,
getDictionaryInput,
getDictionaryTitles,
getLoading,
getRackTile,
getResult,
getSettingOption,
getSettingsButton,
getTooltip,
solve,
typeRack,
visitIndex,
} from '../support';

it('has title', () => {
visitIndex();

cy.title().should('equal', 'Scrabble Solver 2 by Kamil Mielnik');
});

it('has default setting values', () => {
visitIndex();
getSettingsButton().realClick();

getSettingOption('Game', 'Scrabble').should('be.checked');
getSettingOption('Language', 'English (US)').should('be.checked');
getSettingOption('Coordinates', 'Hidden').should('be.checked');
getSettingOption('Input mode', 'Keyboard').should('be.checked');
getSettingOption('Group remaining tiles', 'Do not group').should('be.checked');
});

describe('full app test', () => {
beforeEach(() => {
cy.intercept('/api/solve').as('solve');
cy.intercept('/api/dictionary/**/*').as('dictionary');
});

it('Scrabble - Polish', () => {
visitIndex();
getSettingsButton().realClick();
getSettingOption('Language', 'Polski').check();
getSettingOption('Współrzędne', 'Oryginalne').check();
cy.realPress('Escape');
typeRack('abł');
solve();

assertResult(0, 'bał', 14);
getResult(0).realHover();
getLoading().should('be.visible');
cy.wait('@dictionary');
getRackTile(0).parent().should('have.attr', 'role', 'mark');
getRackTile(1).parent().should('have.attr', 'role', 'mark');
getRackTile(2).parent().should('have.attr', 'role', 'mark');
getBoardTile(5, 7).should('have.value', 'b');
getBoardTile(6, 7).should('have.value', 'a');
getBoardTile(7, 7).should('have.value', 'ł');
getBoardTile(5, 7).parent().should('have.attr', 'role', 'mark');
getBoardTile(6, 7).parent().should('have.attr', 'role', 'mark');
getBoardTile(7, 7).parent().should('have.attr', 'role', 'mark');
getDictionaryInput().should('have.value', 'bał');
getLoading().should('not.exist');
getDictionaryTitles().should('have.length', 1).and('have.text', 'bał');
getDictionary()
.should('include.text', 'bać się')
.and('include.text', 'odczuwać lęk, strach')
.and('include.text', 'być niespokojnym o kogoś lub o coś')
.and('include.text', 'nie śmieć, nie odważać się na coś');

cy.findByLabelText('Punkty').realClick();
getTooltip().should('be.visible').and('have.text', 'Punkty');
assertResult(0, 'ba', 8);
getResult(0).realHover();
getRackTile(0).parent().should('have.attr', 'role', 'mark');
getRackTile(1).parent().should('have.attr', 'role', 'mark');
getRackTile(2).parent().should('not.have.attr', 'role', 'mark');
getBoardTile(5, 7).should('not.have.value');
getBoardTile(6, 7).should('have.value', 'b');
getBoardTile(7, 7).should('have.value', 'a');
getBoardTile(5, 7).parent().should('not.have.attr', 'role', 'mark');
getBoardTile(6, 7).parent().should('have.attr', 'role', 'mark');
getBoardTile(7, 7).parent().should('have.attr', 'role', 'mark');
getDictionaryInput().should('have.value', 'ba');
getLoading().should('be.visible');
cy.wait('@dictionary');
getLoading().should('not.exist');
getDictionaryTitles().should('have.length', 1).and('have.text', 'ba');
getDictionary()
.should('include.text', 'wykrzyknik, który wyraża głównie podziw, zdziwienie')
.and('include.text', 'w wierzeniach staroegipskich: dusza ludzka ginąca wraz z ciałem');

getResult(0).realClick();
getRackTile(0).parent().should('not.have.attr', 'role', 'mark');
getRackTile(1).parent().should('not.have.attr', 'role', 'mark');
getRackTile(2).parent().should('not.have.attr', 'role', 'mark');
getRackTile(0).should('not.have.value');
getRackTile(1).should('not.have.value');
getRackTile(2).should('have.value', 'ł');
getBoardTile(6, 7).should('have.value', 'b');
getBoardTile(7, 7).should('have.value', 'a');

cy.findByLabelText('Rozwiąż').should('be.visible').and('be.enabled');
});
});
21 changes: 21 additions & 0 deletions cypress/e2e/bugs/112.spec.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { assertResult, getSettingOption, getSettingsButton, solve, typeRack, visitIndex } from '../../support';

/*
* @see https://github.com/kamilmielnik/scrabble-solver/issues/112
*/
it('Scrabble - Character bonus not applied (#112)', () => {
visitIndex();

getSettingsButton().realClick();
getSettingOption('Language', 'Français').check();
cy.realPress('Escape');
typeRack('jours');
solve();

assertResult(0, 'jours', 40);
assertResult(1, 'jours', 40);
assertResult(2, 'jours', 26);
assertResult(3, 'jours', 26);
assertResult(4, 'jours', 24);
assertResult(5, 'jours', 24);
});
14 changes: 14 additions & 0 deletions cypress/e2e/bugs/129.spec.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { getModal, getSettingsButton, typeRack, visitIndex } from '../../support';

/*
* @see https://github.com/kamilmielnik/scrabble-solver/issues/129
*/
it('Esc does not close the sidebar when letters input is focused (#129)', () => {
visitIndex();

getSettingsButton().realClick();
typeRack('a');
cy.realPress('Escape');

getModal().should('not.exist');
});
15 changes: 15 additions & 0 deletions cypress/e2e/bugs/2.spec.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { assertResult, getResults, solve, typeBoard, typeRack, visitIndex } from '../../support';

/*
* @see https://github.com/kamilmielnik/scrabble-solver/issues/2
*/
it('"Q" tile does not work (#2)', () => {
visitIndex();
typeBoard('i', 7, 7);
typeRack('q');
solve();

getResults().should('have.length', 2);
assertResult(0, 'qi', 11);
assertResult(1, 'qi', 11);
});
15 changes: 15 additions & 0 deletions cypress/e2e/bugs/3.spec.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { getSettingOption, getSettingsButton, typeBoard, typeRack, visitIndex } from '../../support';

/*
* @see https://github.com/kamilmielnik/scrabble-solver/issues/3
*/
it('X tile is allowed in Polish language (#3)', () => {
visitIndex();
getSettingsButton().realClick();
getSettingOption('Language', 'Polski').check();
cy.realPress('Escape');
typeBoard('x', 7, 7);
typeRack('x');

cy.findByText('x').should('not.exist');
});
4 changes: 4 additions & 0 deletions cypress/support/commands.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/// <reference types="cypress" />

import '@testing-library/cypress/add-commands';
import 'cypress-real-events';
1 change: 1 addition & 0 deletions cypress/support/e2e.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import './commands';
1 change: 1 addition & 0 deletions cypress/support/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './lib';
Loading

0 comments on commit e7f7138

Please sign in to comment.