scrabble-solver 2.13.12 → 2.13.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (135) hide show
  1. package/.eslintrc.js +13 -4
  2. package/.github/workflows/cypress.yml +24 -0
  3. package/.github/workflows/eslint.yml +9 -10
  4. package/.github/workflows/jest.yml +27 -0
  5. package/.nx/cache/nx_files.nxt +0 -0
  6. package/.nx/workspace-data/file-map.json +632 -548
  7. package/.nx/workspace-data/lockfile.hash +1 -1
  8. package/.nx/workspace-data/parsed-lock-file.json +5765 -5261
  9. package/.nx/workspace-data/project-graph.json +4745 -3485
  10. package/README.md +5 -3
  11. package/cypress/e2e/app.spec.cy.ts +107 -0
  12. package/cypress/e2e/bugs/112.spec.cy.ts +29 -0
  13. package/cypress/e2e/bugs/129.spec.cy.ts +13 -0
  14. package/cypress/e2e/bugs/2.spec.cy.ts +15 -0
  15. package/cypress/e2e/bugs/3.spec.cy.ts +15 -0
  16. package/cypress/e2e/bugs/342.spec.cy.ts +37 -0
  17. package/cypress/e2e/bugs/348.spec.cy.ts +73 -0
  18. package/cypress/support/commands.ts +4 -0
  19. package/cypress/support/e2e.ts +1 -0
  20. package/cypress/support/index.ts +1 -0
  21. package/cypress/support/lib/actions.ts +57 -0
  22. package/cypress/support/lib/index.ts +2 -0
  23. package/cypress/support/lib/selectors.ts +70 -0
  24. package/cypress/tsconfig.json +9 -0
  25. package/cypress.config.ts +9 -0
  26. package/jest.config.js +2 -3
  27. package/lerna.json +1 -1
  28. package/package.json +27 -20
  29. package/packages/configs/package.json +3 -3
  30. package/packages/constants/package.json +1 -1
  31. package/packages/dictionaries/package-lock.json +2 -2
  32. package/packages/dictionaries/package.json +4 -4
  33. package/packages/logger/package-lock.json +3 -3
  34. package/packages/logger/package.json +2 -2
  35. package/packages/scrabble-solver/.next/BUILD_ID +1 -1
  36. package/packages/scrabble-solver/.next/build-manifest.json +14 -14
  37. package/packages/scrabble-solver/.next/cache/.tsbuildinfo +1 -1
  38. package/packages/scrabble-solver/.next/cache/eslint/.cache_8dgz12 +1 -1
  39. package/packages/scrabble-solver/.next/cache/webpack/client-production/0.pack +0 -0
  40. package/packages/scrabble-solver/.next/cache/webpack/client-production/index.pack +0 -0
  41. package/packages/scrabble-solver/.next/cache/webpack/client-production/index.pack.old +0 -0
  42. package/packages/scrabble-solver/.next/cache/webpack/edge-server-production/0.pack +0 -0
  43. package/packages/scrabble-solver/.next/cache/webpack/edge-server-production/index.pack +0 -0
  44. package/packages/scrabble-solver/.next/cache/webpack/edge-server-production/index.pack.old +0 -0
  45. package/packages/scrabble-solver/.next/cache/webpack/server-production/0.pack +0 -0
  46. package/packages/scrabble-solver/.next/cache/webpack/server-production/index.pack +0 -0
  47. package/packages/scrabble-solver/.next/cache/webpack/server-production/index.pack.old +0 -0
  48. package/packages/scrabble-solver/.next/prerender-manifest.js +1 -1
  49. package/packages/scrabble-solver/.next/prerender-manifest.json +1 -1
  50. package/packages/scrabble-solver/.next/routes-manifest.json +1 -1
  51. package/packages/scrabble-solver/.next/server/chunks/807.js +1 -1
  52. package/packages/scrabble-solver/.next/server/middleware-build-manifest.js +1 -1
  53. package/packages/scrabble-solver/.next/server/pages/404.html +1 -1
  54. package/packages/scrabble-solver/.next/server/pages/500.html +1 -1
  55. package/packages/scrabble-solver/.next/server/pages/api/solve.js +1 -1
  56. package/packages/scrabble-solver/.next/server/pages/index.html +1 -1
  57. package/packages/scrabble-solver/.next/server/pages/index.js +1 -1
  58. package/packages/scrabble-solver/.next/server/pages/index.json +1 -1
  59. package/packages/scrabble-solver/.next/server/pages-manifest.json +1 -1
  60. package/packages/scrabble-solver/.next/static/chunks/{main-b5b360c6afb66b05.js → main-8b0b4e610892a916.js} +1 -1
  61. package/packages/scrabble-solver/.next/static/chunks/pages/{404-0c9f3e0f8b15f487.js → 404-b0c2ccded2455be0.js} +1 -1
  62. package/packages/scrabble-solver/.next/static/chunks/pages/_app-42ce6b4032e931ff.js +17 -0
  63. package/packages/scrabble-solver/.next/static/chunks/pages/index-3718075f2ba2220c.js +1 -0
  64. package/packages/scrabble-solver/.next/static/chunks/webpack-c4acd79e87956a0e.js +1 -0
  65. package/packages/scrabble-solver/.next/static/css/2adc9736d823979b.css +2 -0
  66. package/packages/scrabble-solver/.next/static/{N8hSsS6Ppzlj3ebHMSZvI → qwJjm2FeDHHGY92CY5oQQ}/_buildManifest.js +1 -1
  67. package/packages/scrabble-solver/.next/trace +45 -45
  68. package/packages/scrabble-solver/package-lock.json +6 -6
  69. package/packages/scrabble-solver/package.json +12 -12
  70. package/packages/scrabble-solver/public/service-worker.js +1 -1
  71. package/packages/scrabble-solver/src/components/Board/Board.module.scss +3 -9
  72. package/packages/scrabble-solver/src/components/Board/Board.tsx +2 -2
  73. package/packages/scrabble-solver/src/components/Board/BoardPure.tsx +3 -2
  74. package/packages/scrabble-solver/src/components/Board/components/ToggleDirectionButton/ToggleDirectionButton.tsx +2 -0
  75. package/packages/scrabble-solver/src/components/Board/hooks/useBackgroundImage.tsx +2 -6
  76. package/packages/scrabble-solver/src/components/Board/hooks/useBoardStyle.ts +10 -4
  77. package/packages/scrabble-solver/src/components/Dictionary/Dictionary.module.scss +10 -7
  78. package/packages/scrabble-solver/src/components/Dictionary/Dictionary.tsx +38 -36
  79. package/packages/scrabble-solver/src/components/Loading/Loading.module.scss +1 -0
  80. package/packages/scrabble-solver/src/components/Loading/Loading.tsx +1 -1
  81. package/packages/scrabble-solver/src/components/Modal/components/Section/Section.tsx +3 -2
  82. package/packages/scrabble-solver/src/components/NavButtons/NavButtons.tsx +1 -0
  83. package/packages/scrabble-solver/src/components/Rack/Rack.tsx +1 -0
  84. package/packages/scrabble-solver/src/components/Results/Cell.tsx +7 -3
  85. package/packages/scrabble-solver/src/components/Results/Header.tsx +99 -0
  86. package/packages/scrabble-solver/src/components/Results/HeaderButton.tsx +18 -13
  87. package/packages/scrabble-solver/src/components/Results/Result.tsx +23 -16
  88. package/packages/scrabble-solver/src/components/Results/Results.module.scss +24 -12
  89. package/packages/scrabble-solver/src/components/Results/Results.tsx +3 -9
  90. package/packages/scrabble-solver/src/components/Results/types.ts +0 -8
  91. package/packages/scrabble-solver/src/components/Solver/Solver.tsx +1 -1
  92. package/packages/scrabble-solver/src/components/Tile/TilePure.tsx +1 -0
  93. package/packages/scrabble-solver/src/hooks/index.ts +1 -0
  94. package/packages/scrabble-solver/src/hooks/useAppLayout.ts +12 -1
  95. package/packages/scrabble-solver/src/hooks/useColumns.ts +47 -0
  96. package/packages/scrabble-solver/src/icons/GeoAlt.svg +5 -0
  97. package/packages/scrabble-solver/src/icons/OneTwoThree.svg +4 -0
  98. package/packages/scrabble-solver/src/icons/SquareA.svg +6 -0
  99. package/packages/scrabble-solver/src/icons/SquareB.svg +6 -0
  100. package/packages/scrabble-solver/src/icons/Squares.svg +34 -0
  101. package/packages/scrabble-solver/src/icons/Words.svg +22 -0
  102. package/packages/scrabble-solver/src/icons/index.ts +6 -0
  103. package/packages/scrabble-solver/src/lib/groupResults.ts +1 -1
  104. package/packages/scrabble-solver/src/lib/index.ts +0 -1
  105. package/packages/scrabble-solver/src/lib/sortResults.ts +10 -10
  106. package/packages/scrabble-solver/src/modals/KeyMapModal/KeyMapModal.tsx +8 -9
  107. package/packages/scrabble-solver/src/modals/RemainingTilesModal/RemainingTilesModal.tsx +1 -0
  108. package/packages/scrabble-solver/src/modals/SettingsModal/SettingsModal.tsx +5 -5
  109. package/packages/scrabble-solver/src/modals/WordsModal/WordsModal.tsx +2 -0
  110. package/packages/scrabble-solver/src/parameters/index.ts +12 -0
  111. package/packages/scrabble-solver/src/state/selectors.ts +26 -1
  112. package/packages/scrabble-solver/src/state/slices/resultsInitialState.ts +2 -2
  113. package/packages/scrabble-solver/src/state/slices/resultsSlice.ts +2 -2
  114. package/packages/scrabble-solver/src/state/useTranslate.ts +5 -1
  115. package/packages/scrabble-solver/src/styles/variables.scss +1 -0
  116. package/packages/scrabble-solver/src/types/index.ts +11 -2
  117. package/packages/solver/package-lock.json +2 -2
  118. package/packages/solver/package.json +5 -5
  119. package/packages/types/package.json +2 -2
  120. package/packages/word-definitions/build/lib/request.d.ts +0 -1
  121. package/packages/word-definitions/package-lock.json +2 -2
  122. package/packages/word-definitions/package.json +2 -2
  123. package/packages/word-lists/package-lock.json +2 -2
  124. package/packages/word-lists/package.json +2 -2
  125. package/tsconfig.jest.json +2 -1
  126. package/.github/workflows/test.yml +0 -28
  127. package/packages/scrabble-solver/.next/static/chunks/pages/_app-8246f5b39b6a5e59.js +0 -17
  128. package/packages/scrabble-solver/.next/static/chunks/pages/index-65bfe83d121535ab.js +0 -1
  129. package/packages/scrabble-solver/.next/static/chunks/webpack-6ef43a8d4a395f49.js +0 -1
  130. package/packages/scrabble-solver/.next/static/css/2f727b21d1331ea5.css +0 -2
  131. package/packages/scrabble-solver/src/components/Results/getCoordinatesColumn.ts +0 -14
  132. package/packages/scrabble-solver/src/components/Results/getLocaleColumns.ts +0 -58
  133. package/packages/scrabble-solver/src/components/Results/useColumns.ts +0 -44
  134. package/packages/scrabble-solver/src/lib/dataUrlToBlob.ts +0 -20
  135. /package/packages/scrabble-solver/.next/static/{N8hSsS6Ppzlj3ebHMSZvI → qwJjm2FeDHHGY92CY5oQQ}/_ssgManifest.js +0 -0
package/README.md CHANGED
@@ -39,9 +39,10 @@
39
39
  </p>
40
40
 
41
41
  <p>
42
- <img src="https://github.com/kamilmielnik/scrabble-solver/workflows/Build/badge.svg" alt="Build" />
43
- <img src="https://github.com/kamilmielnik/scrabble-solver/workflows/Test/badge.svg" alt="Test" />
44
- <img src="https://github.com/kamilmielnik/scrabble-solver/workflows/ESLint/badge.svg" alt="ESLint" />
42
+ <img src="https://github.com/kamilmielnik/scrabble-solver/actions/workflows/build.yml/badge.svg" alt="Build" />
43
+ <img src="https://github.com/kamilmielnik/scrabble-solver/actions/workflows/jest.yml/badge.svg" alt="Jest Tests" />
44
+ <img src="https://github.com/kamilmielnik/scrabble-solver/actions/workflows/cypress.yml/badge.svg" alt="Cypress Tests" />
45
+ <img src="https://github.com/kamilmielnik/scrabble-solver/actions/workflows/eslint.yml/badge.svg" alt="ESLint" />
45
46
  </p>
46
47
 
47
48
  <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" />
@@ -183,6 +184,7 @@ npm run build -w @scrabble-solver/word-lists
183
184
  - [CSS Modules](https://github.com/css-modules/css-modules)
184
185
  - [include-media](https://eduardoboucas.github.io/include-media/)
185
186
  - [Lerna](https://lerna.js.org/)
187
+ - [Cypress](https://www.cypress.io/)
186
188
  - [Jest](https://jestjs.io/)
187
189
  - [ESLint](https://eslint.org/)
188
190
  - [Prettier](https://prettier.io/)
@@ -0,0 +1,107 @@
1
+ import {
2
+ assertResult,
3
+ closeModal,
4
+ getBoardTile,
5
+ getDictionary,
6
+ getDictionaryInput,
7
+ getDictionaryTitles,
8
+ getLoading,
9
+ getRackTile,
10
+ getResult,
11
+ getSettingOption,
12
+ getSettingsButton,
13
+ getTooltip,
14
+ solve,
15
+ typeRack,
16
+ visitIndex,
17
+ } from '../support';
18
+
19
+ it('has title', () => {
20
+ visitIndex();
21
+
22
+ cy.title().should('equal', 'Scrabble Solver 2 by Kamil Mielnik');
23
+ });
24
+
25
+ it('has default setting values', () => {
26
+ visitIndex();
27
+ getSettingsButton().realClick();
28
+
29
+ getSettingOption('Game', 'Scrabble').should('be.checked');
30
+ getSettingOption('Language', 'English (US)').should('be.checked');
31
+ getSettingOption('Coordinates', 'Hidden').should('be.checked');
32
+ getSettingOption('Input mode', 'Keyboard').should('be.checked');
33
+ getSettingOption('Group remaining tiles', 'Do not group').should('be.checked');
34
+ });
35
+
36
+ describe('full app test', () => {
37
+ beforeEach(() => {
38
+ cy.intercept('/api/solve').as('solve');
39
+ cy.intercept('/api/dictionary/**/*').as('dictionary');
40
+ });
41
+
42
+ it('Scrabble - Polish', () => {
43
+ visitIndex();
44
+ getSettingsButton().realClick();
45
+ getSettingOption('Language', 'Polski').check();
46
+ getSettingOption('Współrzędne', 'Oryginalne').check();
47
+ closeModal();
48
+ typeRack('abł');
49
+ solve();
50
+
51
+ assertResult(0, 'bał', 14);
52
+ getResult(0).realHover();
53
+ getLoading().should('be.visible');
54
+ cy.wait('@dictionary');
55
+ getRackTile(0).parent().should('have.attr', 'role', 'mark');
56
+ getRackTile(1).parent().should('have.attr', 'role', 'mark');
57
+ getRackTile(2).parent().should('have.attr', 'role', 'mark');
58
+ getBoardTile(5, 7).should('have.value', 'b');
59
+ getBoardTile(6, 7).should('have.value', 'a');
60
+ getBoardTile(7, 7).should('have.value', 'ł');
61
+ getBoardTile(5, 7).parent().should('have.attr', 'role', 'mark');
62
+ getBoardTile(6, 7).parent().should('have.attr', 'role', 'mark');
63
+ getBoardTile(7, 7).parent().should('have.attr', 'role', 'mark');
64
+ getDictionaryInput().should('have.value', 'bał');
65
+ getLoading().should('not.exist');
66
+ getDictionaryTitles().should('have.length', 1).and('have.text', 'bał');
67
+ getDictionary()
68
+ .should('include.text', 'bać się')
69
+ .and('include.text', 'odczuwać lęk, strach')
70
+ .and('include.text', 'być niespokojnym o kogoś lub o coś')
71
+ .and('include.text', 'nie śmieć, nie odważać się na coś');
72
+
73
+ cy.findByLabelText('Punkty').realClick();
74
+ getTooltip().should('be.visible').and('have.text', 'Punkty');
75
+ assertResult(0, 'ba', 8);
76
+ getResult(0).realHover();
77
+ getRackTile(0).parent().should('have.attr', 'role', 'mark');
78
+ getRackTile(1).parent().should('have.attr', 'role', 'mark');
79
+ getRackTile(2).parent().should('not.have.attr', 'role', 'mark');
80
+ getBoardTile(5, 7).should('not.have.value');
81
+ getBoardTile(6, 7).should('have.value', 'b');
82
+ getBoardTile(7, 7).should('have.value', 'a');
83
+ getBoardTile(5, 7).parent().should('not.have.attr', 'role', 'mark');
84
+ getBoardTile(6, 7).parent().should('have.attr', 'role', 'mark');
85
+ getBoardTile(7, 7).parent().should('have.attr', 'role', 'mark');
86
+ getDictionaryInput().should('have.value', 'ba');
87
+ getLoading().should('be.visible');
88
+ cy.wait('@dictionary');
89
+ getLoading().should('not.exist');
90
+ getDictionaryTitles().should('have.length', 1).and('have.text', 'ba');
91
+ getDictionary()
92
+ .should('include.text', 'wykrzyknik, który wyraża głównie podziw, zdziwienie')
93
+ .and('include.text', 'w wierzeniach staroegipskich: dusza ludzka ginąca wraz z ciałem');
94
+
95
+ getResult(0).realClick();
96
+ getRackTile(0).parent().should('not.have.attr', 'role', 'mark');
97
+ getRackTile(1).parent().should('not.have.attr', 'role', 'mark');
98
+ getRackTile(2).parent().should('not.have.attr', 'role', 'mark');
99
+ getRackTile(0).should('not.have.value');
100
+ getRackTile(1).should('not.have.value');
101
+ getRackTile(2).should('have.value', 'ł');
102
+ getBoardTile(6, 7).should('have.value', 'b');
103
+ getBoardTile(7, 7).should('have.value', 'a');
104
+
105
+ cy.findByLabelText('Rozwiąż').should('be.visible').and('be.enabled');
106
+ });
107
+ });
@@ -0,0 +1,29 @@
1
+ import {
2
+ assertResult,
3
+ closeModal,
4
+ getSettingOption,
5
+ getSettingsButton,
6
+ solve,
7
+ typeRack,
8
+ visitIndex,
9
+ } from '../../support';
10
+
11
+ /*
12
+ * @see https://github.com/kamilmielnik/scrabble-solver/issues/112
13
+ */
14
+ it('Scrabble - Character bonus not applied (#112)', () => {
15
+ visitIndex();
16
+
17
+ getSettingsButton().realClick();
18
+ getSettingOption('Language', 'Français').check();
19
+ closeModal();
20
+ typeRack('jours');
21
+ solve();
22
+
23
+ assertResult(0, 'jours', 40);
24
+ assertResult(1, 'jours', 40);
25
+ assertResult(2, 'jours', 26);
26
+ assertResult(3, 'jours', 26);
27
+ assertResult(4, 'jours', 24);
28
+ assertResult(5, 'jours', 24);
29
+ });
@@ -0,0 +1,13 @@
1
+ import { getModal, getSettingsButton, typeRack, visitIndex } from '../../support';
2
+
3
+ /*
4
+ * @see https://github.com/kamilmielnik/scrabble-solver/issues/129
5
+ */
6
+ it('Esc does not close the sidebar when letters input is focused (#129)', () => {
7
+ visitIndex();
8
+ getSettingsButton().realClick();
9
+ typeRack('a');
10
+ cy.realPress('Escape');
11
+
12
+ getModal().should('not.exist');
13
+ });
@@ -0,0 +1,15 @@
1
+ import { assertResult, getResults, solve, typeBoard, typeRack, visitIndex } from '../../support';
2
+
3
+ /*
4
+ * @see https://github.com/kamilmielnik/scrabble-solver/issues/2
5
+ */
6
+ it('"Q" tile does not work (#2)', () => {
7
+ visitIndex();
8
+ typeBoard('i', 'horizontal', 7, 7);
9
+ typeRack('q');
10
+ solve();
11
+
12
+ getResults().should('have.length', 2);
13
+ assertResult(0, 'qi', 11);
14
+ assertResult(1, 'qi', 11);
15
+ });
@@ -0,0 +1,15 @@
1
+ import { closeModal, getSettingOption, getSettingsButton, typeBoard, typeRack, visitIndex } from '../../support';
2
+
3
+ /*
4
+ * @see https://github.com/kamilmielnik/scrabble-solver/issues/3
5
+ */
6
+ it('X tile is allowed in Polish language (#3)', () => {
7
+ visitIndex();
8
+ getSettingsButton().realClick();
9
+ getSettingOption('Language', 'Polski').check();
10
+ closeModal();
11
+ typeBoard('x', 'horizontal', 7, 7);
12
+ typeRack('x');
13
+
14
+ cy.findByText('x').should('not.exist');
15
+ });
@@ -0,0 +1,37 @@
1
+ import {
2
+ assertResult,
3
+ closeModal,
4
+ getResult,
5
+ getResultsFilterInput,
6
+ getSettingOption,
7
+ getSettingsButton,
8
+ solve,
9
+ typeBoard,
10
+ typeRack,
11
+ visitIndex,
12
+ } from '../../support';
13
+
14
+ /*
15
+ * @see https://github.com/kamilmielnik/scrabble-solver/issues/342
16
+ */
17
+ it('Incorrect sorting when filtering (#342)', () => {
18
+ visitIndex();
19
+ getSettingsButton().realClick();
20
+ getSettingOption('Language', 'Polski').check();
21
+ closeModal();
22
+ typeBoard('bopie', 'horizontal', 6, 3);
23
+ typeBoard('apu', 'vertical', 8, 4);
24
+ typeBoard('o', 'vertical', 10, 4);
25
+ typeRack('oe');
26
+ solve();
27
+ getResultsFilterInput().type('p');
28
+
29
+ assertResult(0, 'po', 5);
30
+ assertResult(1, 'pe', 5);
31
+ assertResult(2, 'op', 3);
32
+ assertResult(3, 'eo', 6);
33
+ getResult(0).should('not.have.attr', 'aria-hidden');
34
+ getResult(1).should('not.have.attr', 'aria-hidden');
35
+ getResult(2).should('not.have.attr', 'aria-hidden');
36
+ getResult(3).should('have.attr', 'aria-hidden', 'true');
37
+ });
@@ -0,0 +1,73 @@
1
+ import {
2
+ getResult,
3
+ getSettingOption,
4
+ getSettingsButton,
5
+ solve,
6
+ typeRack,
7
+ unregisterServiceWorkers,
8
+ visitIndex,
9
+ } from '../../support';
10
+
11
+ /*
12
+ * @see https://github.com/kamilmielnik/scrabble-solver/issues/348
13
+ */
14
+ describe('#348', () => {
15
+ beforeEach(async () => {
16
+ await unregisterServiceWorkers();
17
+ });
18
+
19
+ it('Missing result word padding when coordinates are not shown (LTR) (#348)', () => {
20
+ visitIndex();
21
+ typeRack('ab');
22
+ solve();
23
+
24
+ getResult(0).then(([$result]) => {
25
+ const $span = $result.children[0];
26
+ const firstCell = $span.children[0];
27
+ const lastCell = $span.children[$span.children.length - 1];
28
+
29
+ expect(window.getComputedStyle(firstCell).paddingLeft).to.equal('15px');
30
+ expect(window.getComputedStyle(lastCell).paddingRight).to.equal('5px');
31
+ });
32
+
33
+ getSettingsButton().realClick();
34
+ getSettingOption('Coordinates', 'Original').check();
35
+
36
+ getResult(0).then(([$result]) => {
37
+ const $span = $result.children[0];
38
+ const firstCell = $span.children[0];
39
+ const lastCell = $span.children[$span.children.length - 1];
40
+
41
+ expect(window.getComputedStyle(firstCell).paddingLeft).to.equal('5px');
42
+ expect(window.getComputedStyle(lastCell).paddingRight).to.equal('5px');
43
+ });
44
+ });
45
+
46
+ it('Missing result word padding when coordinates are not shown (RTL) (#348)', () => {
47
+ visitIndex();
48
+ getSettingsButton().realClick();
49
+ getSettingOption('Language', 'فارسی').check();
50
+ typeRack('فا');
51
+ solve();
52
+
53
+ getResult(0).then(([$result]) => {
54
+ const $span = $result.children[0];
55
+ const firstCell = $span.children[0];
56
+ const lastCell = $span.children[$span.children.length - 1];
57
+
58
+ expect(window.getComputedStyle(firstCell).paddingRight).to.equal('15px');
59
+ expect(window.getComputedStyle(lastCell).paddingLeft).to.equal('5px');
60
+ });
61
+
62
+ getSettingOption('مختصات', 'إبداعي').check();
63
+
64
+ getResult(0).then(([$result]) => {
65
+ const $span = $result.children[0];
66
+ const firstCell = $span.children[0];
67
+ const lastCell = $span.children[$span.children.length - 1];
68
+
69
+ expect(window.getComputedStyle(firstCell).paddingRight).to.equal('5px');
70
+ expect(window.getComputedStyle(lastCell).paddingLeft).to.equal('5px');
71
+ });
72
+ });
73
+ });
@@ -0,0 +1,4 @@
1
+ /// <reference types="cypress" />
2
+
3
+ import '@testing-library/cypress/add-commands';
4
+ import 'cypress-real-events';
@@ -0,0 +1 @@
1
+ import './commands';
@@ -0,0 +1 @@
1
+ export * from './lib';
@@ -0,0 +1,57 @@
1
+ import { getBoardTile, getLoading, getModal, getRackTile, getResult } from './selectors';
2
+
3
+ const getRandomId = () => {
4
+ return String(Math.random()).replace(/^\d\./, '');
5
+ };
6
+
7
+ export const visitIndex = () => {
8
+ cy.visit('/');
9
+ };
10
+
11
+ export const closeModal = () => {
12
+ getModal().should('be.visible');
13
+ cy.realPress('Escape');
14
+ getModal().should('not.exist');
15
+ };
16
+
17
+ export const typeRack = (tiles: string, index = 0) => {
18
+ getRackTile(index).focus().type(tiles);
19
+ };
20
+
21
+ export const typeBoard = (tiles: string, direction: 'horizontal' | 'vertical', x = 0, y = 0) => {
22
+ getBoardTile(x, y).focus();
23
+
24
+ cy.findByTestId('toggle-direction-button').then(([$button]) => {
25
+ if ($button.dataset.direction !== direction) {
26
+ cy.wrap($button).click();
27
+ }
28
+ });
29
+
30
+ for (let index = 0; index < tiles.length; ++index) {
31
+ const xOffset = direction === 'horizontal' ? index : 0;
32
+ const yOffset = direction === 'vertical' ? index : 0;
33
+
34
+ getBoardTile(x + xOffset, y + yOffset)
35
+ .focus()
36
+ .type(tiles[index]);
37
+ }
38
+ };
39
+
40
+ export const solve = () => {
41
+ const id = `solve-${getRandomId()}`;
42
+
43
+ cy.intercept('/api/solve').as(id);
44
+ getRackTile().focus().parents('form').submit();
45
+ cy.wait(`@${id}`);
46
+ getLoading().should('not.exist');
47
+ };
48
+
49
+ export const assertResult = (index: number, word: string, points: number) => {
50
+ getResult(index).should('have.attr', 'aria-label', word).and('include.text', word);
51
+ getResult(index).findByTestId('points').should('have.text', String(points));
52
+ };
53
+
54
+ export const unregisterServiceWorkers = async () => {
55
+ const registrations = await navigator.serviceWorker.getRegistrations();
56
+ return Promise.all(registrations.map((registration) => registration.unregister()));
57
+ };
@@ -0,0 +1,2 @@
1
+ export * from './actions';
2
+ export * from './selectors';
@@ -0,0 +1,70 @@
1
+ export const getModal = () => {
2
+ return cy.findByRole('dialog');
3
+ };
4
+
5
+ export const getBoardContainer = () => {
6
+ return cy.findByTestId('board');
7
+ };
8
+
9
+ export const getBoardTile = (x = 0, y = 0) => {
10
+ return getBoardContainer()
11
+ .findAllByRole('textbox')
12
+ .then((tiles) => {
13
+ const boardSize = Math.sqrt(tiles.length);
14
+ const index = y * boardSize + x;
15
+ return cy.findByTestId('board').findAllByRole('textbox').eq(index);
16
+ });
17
+ };
18
+
19
+ export const getDictionary = () => {
20
+ return cy.findByTestId('dictionary');
21
+ };
22
+
23
+ export const getDictionaryInput = () => {
24
+ return getDictionary().findByRole('textbox');
25
+ };
26
+
27
+ export const getDictionaryTitles = () => {
28
+ return getDictionary().get('h2');
29
+ };
30
+
31
+ export const getLoading = () => {
32
+ return cy.findByTestId('loading');
33
+ };
34
+
35
+ export const getRackContainer = () => {
36
+ return cy.findByTestId('rack');
37
+ };
38
+
39
+ export const getRackTile = (index = 0) => {
40
+ // use hidden: true so that rack can be interacted with while modal is opened
41
+ return getRackContainer().findAllByRole('textbox', { hidden: true }).eq(index);
42
+ };
43
+
44
+ export const getResultsContainer = () => {
45
+ return cy.findByTestId('results');
46
+ };
47
+
48
+ export const getResults = () => {
49
+ return getResultsContainer().findAllByTestId('result');
50
+ };
51
+
52
+ export const getResultsFilterInput = () => {
53
+ return getResultsContainer().findByRole('textbox');
54
+ };
55
+
56
+ export const getResult = (index = 0) => {
57
+ return getResults().eq(index);
58
+ };
59
+
60
+ export const getSettingsButton = () => {
61
+ return cy.findByTestId('settings-button');
62
+ };
63
+
64
+ export const getSettingOption = (section: string, option: string) => {
65
+ return getModal().findByLabelText(section).findByLabelText(option);
66
+ };
67
+
68
+ export const getTooltip = () => {
69
+ return cy.findByRole('tooltip');
70
+ };
@@ -0,0 +1,9 @@
1
+ {
2
+ "extends": "../tsconfig.json",
3
+
4
+ "compilerOptions": {
5
+ "noEmit": true,
6
+ "types": ["node", "cypress", "cypress-real-events", "@testing-library/cypress"]
7
+ },
8
+ "include": ["../node_modules/cypress", "./**/*.ts"]
9
+ }
@@ -0,0 +1,9 @@
1
+ import { defineConfig } from 'cypress';
2
+
3
+ export default defineConfig({
4
+ e2e: {
5
+ baseUrl: 'http://localhost:3000',
6
+ viewportHeight: 900,
7
+ viewportWidth: 1440,
8
+ },
9
+ });
package/jest.config.js CHANGED
@@ -1,11 +1,10 @@
1
- const tsConfig = require('./tsconfig.jest.json');
2
-
3
1
  module.exports = {
4
2
  coveragePathIgnorePatterns: ['/node_modules/', '/build/'],
5
3
  preset: 'ts-jest',
6
4
  setupFilesAfterEnv: ['./jest.setup.js'],
7
5
  testEnvironment: 'node',
6
+ testPathIgnorePatterns: ['/node_modules/', '/cypress/'],
8
7
  transform: {
9
- '^.+.tsx?$': ['ts-jest', tsConfig],
8
+ '^.+.tsx?$': ['ts-jest', { tsconfig: 'tsconfig.jest.json' }],
10
9
  },
11
10
  };
package/lerna.json CHANGED
@@ -6,5 +6,5 @@
6
6
  "access": "public"
7
7
  },
8
8
  "tagVersionPrefix": "",
9
- "version": "2.13.12"
9
+ "version": "2.13.13"
10
10
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "scrabble-solver",
3
- "version": "2.13.12",
3
+ "version": "2.13.13",
4
4
  "description": "Scrabble Solver 2 - Free, open-source, cross-platform, and multi-language analysis tool for Scrabble, Super Scrabble & Literaki. Quickly find top scoring words using given board & tiles.",
5
5
  "keywords": [
6
6
  "Scrabble",
@@ -37,7 +37,7 @@
37
37
  "build": "npm run build -w packages",
38
38
  "clean": "npm run clean -w packages && rimraf node_modules/",
39
39
  "dev": "npm run dev -w @scrabble-solver/scrabble-solver",
40
- "lint": "eslint packages/*",
40
+ "lint": "eslint cypress packages",
41
41
  "lint:fix": "npm run lint -- --fix",
42
42
  "prettier": "prettier --check './**/*.{js,ts,tsx,scss}'",
43
43
  "prettier:fix": "npm run prettier -- --write",
@@ -45,12 +45,15 @@
45
45
  "release": "npm run release:prepare && npm run release:publish",
46
46
  "release:prepare": "npm run reinstall",
47
47
  "release:publish": "npm run version:bump && np && lerna publish from-package --yes",
48
- "start": "npm-run-all --parallel start:*",
48
+ "start": "concurrently -n w: npm:start:*",
49
49
  "start:app": "npm run start -w @scrabble-solver/scrabble-solver",
50
50
  "start:browser": "open-cli http://localhost:3333",
51
- "test": "jest --no-cache --coverage",
52
- "test:debug": "node --inspect-brk node_modules/.bin/jest --runInBand --no-cache --watch",
53
- "test:watch": "jest --no-cache --watch",
51
+ "test": "npm run build && npm run test-jest && concurrently \"npm run start:app\" \"npm run test-cypress:run\" -k -s first",
52
+ "test-cypress": "cypress open",
53
+ "test-cypress:run": "CYPRESS_BASE_URL=http://localhost:3333 cypress run",
54
+ "test-jest": "jest --no-cache --coverage",
55
+ "test-jest:debug": "node --inspect-brk node_modules/.bin/jest --runInBand --no-cache --watch",
56
+ "test-jest:watch": "jest --no-cache --watch",
54
57
  "version:bump": "npm run version:bump-lerna && npm run version:commit",
55
58
  "version:bump-lerna": "lerna version --force-publish --no-git-tag-version --no-push",
56
59
  "version:commit": "node bump-version.js && git add . && git commit -m \"Bump version\""
@@ -81,32 +84,36 @@
81
84
  "packages/scrabble-solver"
82
85
  ],
83
86
  "dependencies": {
84
- "@scrabble-solver/scrabble-solver": "^2.13.12",
85
- "npm-run-all": "^4.1.5",
87
+ "@scrabble-solver/scrabble-solver": "^2.13.13",
88
+ "concurrently": "^8.2.2",
86
89
  "open-cli": "^8.0.0"
87
90
  },
88
91
  "devDependencies": {
89
- "@babel/core": "^7.24.7",
92
+ "@babel/core": "^7.24.8",
90
93
  "@babel/plugin-proposal-class-properties": "^7.18.6",
91
- "@babel/preset-env": "^7.24.7",
94
+ "@babel/preset-env": "^7.24.8",
95
+ "@testing-library/cypress": "^10.0.2",
92
96
  "@types/jest": "^29.5.12",
93
- "@types/node": "^20.14.2",
94
- "@typescript-eslint/eslint-plugin": "^7.13.0",
95
- "@typescript-eslint/parser": "^7.13.0",
96
- "babel-eslint": "10.1.0",
97
+ "@types/node": "^20.14.10",
98
+ "@typescript-eslint/eslint-plugin": "^7.16.0",
99
+ "@typescript-eslint/parser": "^7.16.0",
100
+ "cypress": "^13.13.0",
101
+ "cypress-real-events": "^1.13.0",
97
102
  "eslint": "^8.57.0",
98
103
  "eslint-config-prettier": "^9.1.0",
99
104
  "eslint-import-resolver-node": "^0.3.9",
105
+ "eslint-plugin-cypress": "^3.3.0",
100
106
  "eslint-plugin-import": "^2.29.1",
107
+ "eslint-plugin-mocha": "^10.4.3",
101
108
  "eslint-plugin-prettier": "^5.1.3",
102
- "eslint-plugin-react": "^7.34.2",
109
+ "eslint-plugin-react": "^7.34.3",
103
110
  "eslint-plugin-react-hooks": "^4.6.2",
104
111
  "jest": "29.7.0",
105
- "lerna": "^8.1.3",
106
- "np": "^10.0.5",
112
+ "lerna": "^8.1.6",
113
+ "np": "^10.0.6",
107
114
  "prettier": "^3.3.2",
108
- "rimraf": "^5.0.7",
109
- "ts-jest": "^29.1.4",
110
- "typescript": "^5.4.5"
115
+ "rimraf": "^6.0.1",
116
+ "ts-jest": "^29.2.2",
117
+ "typescript": "^5.5.3"
111
118
  }
112
119
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scrabble-solver/configs",
3
- "version": "2.13.12",
3
+ "version": "2.13.13",
4
4
  "description": "Scrabble Solver 2 - Configs",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",
@@ -23,8 +23,8 @@
23
23
  "clean": "rimraf build/"
24
24
  },
25
25
  "dependencies": {
26
- "@scrabble-solver/constants": "^2.13.12",
27
- "@scrabble-solver/types": "^2.13.12"
26
+ "@scrabble-solver/constants": "^2.13.13",
27
+ "@scrabble-solver/types": "^2.13.13"
28
28
  },
29
29
  "gitHead": "1e2d1ad49a288d4a44196d8e3336740da132c637"
30
30
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scrabble-solver/constants",
3
- "version": "2.13.12",
3
+ "version": "2.13.13",
4
4
  "description": "Scrabble Solver 2 - Constants",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@scrabble-solver/dictionaries",
3
- "version": "2.13.12",
3
+ "version": "2.13.13",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@scrabble-solver/dictionaries",
9
- "version": "2.13.12",
9
+ "version": "2.13.13",
10
10
  "license": "CC-BY-NC-ND-4.0",
11
11
  "dependencies": {
12
12
  "@kamilmielnik/trie": "^3.0.0"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scrabble-solver/dictionaries",
3
- "version": "2.13.12",
3
+ "version": "2.13.13",
4
4
  "description": "Scrabble Solver 2 - Dictionaries",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",
@@ -30,9 +30,9 @@
30
30
  },
31
31
  "dependencies": {
32
32
  "@kamilmielnik/trie": "^3.0.0",
33
- "@scrabble-solver/logger": "^2.13.12",
34
- "@scrabble-solver/types": "^2.13.12",
35
- "@scrabble-solver/word-lists": "^2.13.12"
33
+ "@scrabble-solver/logger": "^2.13.13",
34
+ "@scrabble-solver/types": "^2.13.13",
35
+ "@scrabble-solver/word-lists": "^2.13.13"
36
36
  },
37
37
  "gitHead": "1e2d1ad49a288d4a44196d8e3336740da132c637"
38
38
  }