kokopu-react 1.0.2 → 1.3.1

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 (79) hide show
  1. package/.nycrc.yml +13 -0
  2. package/CHANGELOG.md +18 -0
  3. package/README.md +3 -0
  4. package/dist/lib/ArrowMarkerIcon.js +1 -1
  5. package/dist/lib/Chessboard.js +267 -89
  6. package/dist/lib/{impl/ErrorBox.js → ErrorBox.js} +6 -7
  7. package/dist/lib/css/chessboard.css +4 -0
  8. package/dist/lib/i18n.js +35 -0
  9. package/dist/lib/index.js +16 -0
  10. package/doc_src/{demo/.eslintrc.yml → .eslintrc.yml} +0 -0
  11. package/doc_src/demo/PageChessboardBase.js +35 -40
  12. package/doc_src/demo/PageChessboardEdition.js +70 -70
  13. package/doc_src/demo/PageChessboardMove.js +20 -27
  14. package/doc_src/demo/PageChessboardSmallScreens.js +15 -16
  15. package/doc_src/examples/ErrorBox.md +3 -0
  16. package/doc_src/theming/LogoRenderer.js +50 -0
  17. package/doc_src/theming/kokopu-react-logo.png +0 -0
  18. package/doc_src/theming/theming.css +29 -0
  19. package/graphic_test_app/02_chessboard_simple.js +1 -0
  20. package/graphic_test_app/04_chessboard_annotations.js +1 -1
  21. package/graphic_test_app/05_chessboard_move.js +1 -0
  22. package/graphic_test_app/07_chessboard_click_squares.js +36 -0
  23. package/graphic_test_app/08_chessboard_move_pieces.js +36 -0
  24. package/graphic_test_app/09_chessboard_edit_arrows.js +36 -0
  25. package/graphic_test_app/10_chessboard_play_moves.js +39 -0
  26. package/graphic_test_app/11_chessboard_play_promotions.js +35 -0
  27. package/graphic_test_app/common/test_app.js +14 -0
  28. package/graphics/logo-small.svg +14 -0
  29. package/graphics/logo.svg +26 -0
  30. package/package.json +26 -20
  31. package/scripts/test_graphic/docker-compose.yml +1 -1
  32. package/scripts/test_graphic/webpack-config.js +6 -1
  33. package/scripts/test_headless.webpack-config.js +6 -1
  34. package/src/Chessboard.js +156 -40
  35. package/src/{impl/ErrorBox.js → ErrorBox.js} +4 -5
  36. package/src/css/chessboard.css +4 -0
  37. package/src/i18n.js +30 -0
  38. package/src/index.js +2 -0
  39. package/test/4_chessboard_graphic.js +2 -2
  40. package/test/5_chessboard_interaction.js +135 -0
  41. package/test/6_chessboard_play_moves.js +107 -0
  42. package/test/common/graphic.js +75 -15
  43. package/test/references/02_chessboard_simple/2.png +0 -0
  44. package/test/references/02_chessboard_simple/5.png +0 -0
  45. package/test/references/03_chessboard_flipped/2.png +0 -0
  46. package/test/references/05_chessboard_move/8.png +0 -0
  47. package/test/references/08_chessboard_move_pieces/after_move.png +0 -0
  48. package/test/references/08_chessboard_move_pieces/empty_square.png +0 -0
  49. package/test/references/08_chessboard_move_pieces/null_vector.png +0 -0
  50. package/test/references/08_chessboard_move_pieces/out_of_board.png +0 -0
  51. package/test/references/08_chessboard_move_pieces/over_arrow_marker.png +0 -0
  52. package/test/references/08_chessboard_move_pieces/over_empty.png +0 -0
  53. package/test/references/08_chessboard_move_pieces/over_non_empty_1.png +0 -0
  54. package/test/references/08_chessboard_move_pieces/over_non_empty_2.png +0 -0
  55. package/test/references/08_chessboard_move_pieces/over_square_marker.png +0 -0
  56. package/test/references/08_chessboard_move_pieces/over_text_marker.png +0 -0
  57. package/test/references/09_chessboard_edit_arrows/base_1.png +0 -0
  58. package/test/references/09_chessboard_edit_arrows/base_2.png +0 -0
  59. package/test/references/09_chessboard_edit_arrows/edit_color_not_set.png +0 -0
  60. package/test/references/09_chessboard_edit_arrows/null_vector.png +0 -0
  61. package/test/references/09_chessboard_edit_arrows/out_of_board.png +0 -0
  62. package/test/references/09_chessboard_edit_arrows/over_arrow_marker.png +0 -0
  63. package/test/references/09_chessboard_edit_arrows/over_square_marker.png +0 -0
  64. package/test/references/10_chessboard_play_moves/castling_move.png +0 -0
  65. package/test/references/10_chessboard_play_moves/chess960_ambiguous_king_move.png +0 -0
  66. package/test/references/10_chessboard_play_moves/chess960_castling_move_1.png +0 -0
  67. package/test/references/10_chessboard_play_moves/chess960_castling_move_2.png +0 -0
  68. package/test/references/10_chessboard_play_moves/chess960_non_kxr_castling.png +0 -0
  69. package/test/references/10_chessboard_play_moves/illegal_move.png +0 -0
  70. package/test/references/10_chessboard_play_moves/illegal_position.png +0 -0
  71. package/test/references/10_chessboard_play_moves/null_vector.png +0 -0
  72. package/test/references/10_chessboard_play_moves/out_of_board.png +0 -0
  73. package/test/references/10_chessboard_play_moves/regular_move_1.png +0 -0
  74. package/test/references/10_chessboard_play_moves/regular_move_2.png +0 -0
  75. package/test/references/10_chessboard_play_moves/wrong_color.png +0 -0
  76. package/test/references/11_chessboard_play_promotions/antichess_promotion.png +0 -0
  77. package/test/references/11_chessboard_play_promotions/cancel_promotion.png +0 -0
  78. package/test/references/11_chessboard_play_promotions/regular_promotion_1.png +0 -0
  79. package/test/references/11_chessboard_play_promotions/regular_promotion_2.png +0 -0
@@ -0,0 +1,107 @@
1
+ /******************************************************************************
2
+ * *
3
+ * This file is part of Kokopu-React, a JavaScript chess library. *
4
+ * Copyright (C) 2021 Yoann Le Montagner <yo35 -at- melix.net> *
5
+ * *
6
+ * This program is free software: you can redistribute it and/or *
7
+ * modify it under the terms of the GNU Lesser General Public License *
8
+ * as published by the Free Software Foundation, either version 3 of *
9
+ * the License, or (at your option) any later version. *
10
+ * *
11
+ * This program is distributed in the hope that it will be useful, *
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14
+ * GNU Lesser General Public License for more details. *
15
+ * *
16
+ * You should have received a copy of the GNU Lesser General *
17
+ * Public License along with this program. If not, see *
18
+ * <http://www.gnu.org/licenses/>. *
19
+ * *
20
+ ******************************************************************************/
21
+
22
+
23
+ const { openBrowser, closeBrowser, itCustom, setSandbox, compareSandbox, takeScreenshot, compareScreenshot } = require('./common/graphic');
24
+
25
+
26
+ describe('Chessboard play moves', function() {
27
+
28
+ const browserContext = {};
29
+
30
+ before(async function() {
31
+ await openBrowser(this, browserContext);
32
+ });
33
+
34
+ after(async function() {
35
+ await closeBrowser(browserContext);
36
+ });
37
+
38
+ function itCheckPlayMove(itemIndex, label, xFrom, yFrom, xTo, yTo, imageBaseName, expectedText) {
39
+ itCustom(browserContext, '10_chessboard_play_moves', itemIndex, label, async function(element) {
40
+ let actions = browserContext.driver.actions({ async: true });
41
+ let area = await element.getRect();
42
+ await actions.move({ x: area.x + xFrom, y: area.y + yFrom }).press().move({ x: area.x + xTo, y: area.y + yTo }).perform();
43
+ await takeScreenshot(browserContext, imageBaseName, element);
44
+ await actions.release().perform();
45
+ await compareScreenshot(browserContext, imageBaseName);
46
+ await compareSandbox(browserContext, expectedText);
47
+ });
48
+ }
49
+
50
+ itCheckPlayMove(0, 'regular move 1', 225, 335, 220, 225, 'regular_move_1', 'move played: e4');
51
+ itCheckPlayMove(1, 'regular move 2', 275, 225, 135, 85, 'regular_move_2', 'move played: Bxf2+');
52
+ itCheckPlayMove(1, 'castling move', 175, 375, 75, 375, 'castling_move', 'move played: O-O');
53
+ itCheckPlayMove(3, 'chess960 castling move 1', 275, 375, 75, 375, 'chess960_castling_move_1', 'move played: O-O-O');
54
+ itCheckPlayMove(3, 'chess960 castling move 2', 275, 375, 375, 375, 'chess960_castling_move_2', 'move played: O-O');
55
+ itCheckPlayMove(3, 'chess960 ambiguous king move', 275, 375, 320, 375, 'chess960_ambiguous_king_move', 'move played: Kg1');
56
+
57
+ function itCheckNonPlayMove(itemIndex, label, xFrom, yFrom, xTo, yTo, imageBaseName) {
58
+ itCustom(browserContext, '10_chessboard_play_moves', itemIndex, label, async function(element) {
59
+ await setSandbox(browserContext, imageBaseName); // can be any value as long as it is unique among other test-cases
60
+ let actions = browserContext.driver.actions({ async: true });
61
+ let area = await element.getRect();
62
+ await actions.move({ x: area.x + xFrom, y: area.y + yFrom }).press().move({ x: area.x + xTo, y: area.y + yTo }).perform();
63
+ await takeScreenshot(browserContext, imageBaseName, element);
64
+ await actions.release().perform();
65
+ await compareScreenshot(browserContext, imageBaseName);
66
+ await compareSandbox(browserContext, imageBaseName);
67
+ });
68
+ }
69
+
70
+ itCheckNonPlayMove(2, 'illegal position', 225, 325, 225, 225, 'illegal_position');
71
+ itCheckNonPlayMove(0, 'wrong color moved', 75, 25, 125, 125, 'wrong_color');
72
+ itCheckNonPlayMove(0, 'illegal move', 75, 375, 125, 225, 'illegal_move');
73
+ itCheckNonPlayMove(0, 'from == to', 75, 375, 85, 365, 'null_vector');
74
+ itCheckNonPlayMove(0, 'out of board', 325, 375, 450, 285, 'out_of_board');
75
+ itCheckNonPlayMove(3, 'chess960 non-KxR castling', 275, 375, 130, 375, 'chess960_non_kxr_castling');
76
+
77
+ function itCheckPlayPromotion(itemIndex, label, xFrom, yFrom, xTo, yTo, xPromo, yPromo, imageBaseName, expectedText) {
78
+ itCustom(browserContext, '11_chessboard_play_promotions', itemIndex, label, async function(element) {
79
+ let actions = browserContext.driver.actions({ async: true });
80
+ let area = await element.getRect();
81
+ await actions.move({ x: area.x + xFrom, y: area.y + yFrom }).press().move({ x: area.x + xTo, y: area.y + yTo }).release().perform();
82
+ await takeScreenshot(browserContext, imageBaseName, element);
83
+ await actions.move({ x: area.x + xPromo, y: area.y + yPromo }).click().perform();
84
+ await compareScreenshot(browserContext, imageBaseName);
85
+ await compareSandbox(browserContext, expectedText);
86
+ });
87
+ }
88
+
89
+ itCheckPlayPromotion(0, 'regular promotion 1', 75, 75, 75, 25, 60, 10, 'regular_promotion_1', 'promotion move played: b8=Q');
90
+ itCheckPlayPromotion(1, 'regular promotion 2', 75, 325, 25, 375, 15, 280, 'regular_promotion_2', 'promotion move played: bxa1=B');
91
+ itCheckPlayPromotion(2, 'antichess promotion', 325, 325, 325, 375, 325, 175, 'antichess_promotion', 'promotion move played: b8=K');
92
+
93
+ function itCheckNonPlayPromotion(itemIndex, label, xFrom, yFrom, xTo, yTo, xPromo, yPromo, imageBaseName) {
94
+ itCustom(browserContext, '11_chessboard_play_promotions', itemIndex, label, async function(element) {
95
+ await setSandbox(browserContext, imageBaseName); // can be any value as long as it is unique among other test-cases
96
+ let actions = browserContext.driver.actions({ async: true });
97
+ let area = await element.getRect();
98
+ await actions.move({ x: area.x + xFrom, y: area.y + yFrom }).press().move({ x: area.x + xTo, y: area.y + yTo }).release().perform();
99
+ await takeScreenshot(browserContext, imageBaseName, element);
100
+ await actions.move({ x: area.x + xPromo, y: area.y + yPromo }).click().perform();
101
+ await compareScreenshot(browserContext, imageBaseName);
102
+ await compareSandbox(browserContext, imageBaseName);
103
+ });
104
+ }
105
+
106
+ itCheckNonPlayPromotion(1, 'cancel promotion', 75, 325, 75, 375, 190, 220, 'cancel_promotion');
107
+ });
@@ -29,6 +29,7 @@ const test = require('unit.js');
29
29
  const rootDir = __dirname + '/../..';
30
30
  const referenceDir = rootDir + '/test/references';
31
31
  const outputDir = rootDir + '/build/graphic_output';
32
+ const coverageDir = rootDir + '/build/.nyc_output';
32
33
 
33
34
  const UNREACHABLE_TEST_CLIENT_MESSAGE =
34
35
  'Cannot reach the dockerized selenium webbrowser used for graphic tests (probably because the test environment is not running).\n' +
@@ -54,7 +55,6 @@ exports.openBrowser = async function(mochaContext, browserContext) {
54
55
 
55
56
  // Initialize the browser context
56
57
  browserContext.driver = driver;
57
- browserContext.latestTestCase = '';
58
58
  };
59
59
 
60
60
 
@@ -63,6 +63,11 @@ exports.openBrowser = async function(mochaContext, browserContext) {
63
63
  */
64
64
  exports.closeBrowser = async function(browserContext) {
65
65
  if (browserContext.driver) {
66
+
67
+ // Save the coverage data before closing the browser.
68
+ await saveCoverage(browserContext);
69
+
70
+ // Close the browser and clean its context.
66
71
  await browserContext.driver.quit();
67
72
  delete browserContext.driver;
68
73
  delete browserContext.latestTestCase;
@@ -70,6 +75,28 @@ exports.closeBrowser = async function(browserContext) {
70
75
  };
71
76
 
72
77
 
78
+ /**
79
+ * Save the coverage data, if any.
80
+ */
81
+ async function saveCoverage(browserContext) {
82
+ if (!browserContext.latestTestCase) {
83
+ return;
84
+ }
85
+
86
+ // Allocate a filename to save the coverage data.
87
+ let pageIndex = 0;
88
+ let coverageFile = '';
89
+ do {
90
+ coverageFile = `${coverageDir}/${browserContext.latestTestCase}-${pageIndex}.json`;
91
+ ++pageIndex;
92
+ } while (fs.existsSync(coverageFile));
93
+
94
+ // Retrieve and save the coverage data.
95
+ let coverageData = await browserContext.driver.executeScript('return JSON.stringify(window.__coverage__)');
96
+ fs.writeFileSync(coverageFile, coverageData);
97
+ }
98
+
99
+
73
100
  /**
74
101
  * Open the page corresponding to the given test-case.
75
102
  */
@@ -77,24 +104,38 @@ async function fetchTestCase(browserContext, testCaseName) {
77
104
  if (browserContext.latestTestCase === testCaseName) {
78
105
  return;
79
106
  }
107
+
108
+ // Save coverage data before changing the page.
109
+ await saveCoverage(browserContext);
110
+
111
+ // Fetch the new page.
80
112
  await browserContext.driver.get(`file:///app/build/test_graphic/${testCaseName}/index.html`);
81
113
  browserContext.latestTestCase = testCaseName;
82
114
  }
83
115
 
84
116
 
85
117
  /**
86
- * Take a screenshot of the element identified by the given CSS target, and compare it to the reference.
118
+ * Take a screenshot of the element identified by the given CSS target.
87
119
  */
88
- async function takeScreenshotAndCompare(browserContext, imageBaseName, cssTarget) {
120
+ let takeScreenshot = exports.takeScreenshot = async function(browserContext, imageBaseName, element) {
89
121
 
90
- let actualFilename = `${outputDir}/${imageBaseName}.png`;
91
- let expectedFilename = `${referenceDir}/${imageBaseName}.png`;
92
- let differenceFilename = `${outputDir}/${imageBaseName}.diff.png`;
122
+ let actualFilename = `${outputDir}/${browserContext.latestTestCase}/${imageBaseName}.png`;
93
123
 
94
124
  // Take a screenshot of the targeted element.
95
- let image = await browserContext.driver.findElement(By.css(cssTarget)).takeScreenshot();
125
+ let image = await element.takeScreenshot();
96
126
  fs.mkdirSync(path.dirname(actualFilename), { recursive: true });
97
127
  fs.writeFileSync(actualFilename, image, 'base64');
128
+ };
129
+
130
+
131
+ /**
132
+ * Compare a screenshot to its reference.
133
+ */
134
+ let compareScreenshot = exports.compareScreenshot = async function(browserContext, imageBaseName) {
135
+
136
+ let actualFilename = `${outputDir}/${browserContext.latestTestCase}/${imageBaseName}.png`;
137
+ let expectedFilename = `${referenceDir}/${browserContext.latestTestCase}/${imageBaseName}.png`;
138
+ let differenceFilename = `${outputDir}/${browserContext.latestTestCase}/${imageBaseName}.diff.png`;
98
139
 
99
140
  // Compare the current screenshot to the reference.
100
141
  let result = await imgDiff({
@@ -103,16 +144,35 @@ async function takeScreenshotAndCompare(browserContext, imageBaseName, cssTarget
103
144
  diffFilename: differenceFilename,
104
145
  });
105
146
  test.object(result).hasProperty('imagesAreSame', true);
106
- }
147
+ };
148
+
149
+
150
+ /**
151
+ * Compare content of the sandbox to the expected text.
152
+ */
153
+ exports.compareSandbox = async function(browserContext, expectedText) {
154
+ let actualText = await browserContext.driver.findElement(By.id('sandbox')).getText();
155
+ test.value(actualText).is(expectedText);
156
+ };
157
+
158
+
159
+ /**
160
+ * Set the content of the sandbox to the given value.
161
+ */
162
+ exports.setSandbox = async function(browserContext, value) {
163
+ value = value.replace(/\W/g, '');
164
+ await browserContext.driver.executeScript(`document.getElementById("sandbox").innerText = "${value}"`);
165
+ };
107
166
 
108
167
 
109
168
  /**
110
- * Open the page corresponding to the given test-case, take a screenshot, and compare it to the reference.
169
+ * Open the page corresponding to the given test-case, and execute the given scenario.
111
170
  */
112
- exports.itChecksScreenshot = function(browserContext, testCaseName) {
113
- it(testCaseName, async function() {
171
+ let itCustom = exports.itCustom = function(browserContext, testCaseName, itemIndex, itemName, scenario) {
172
+ it(testCaseName + ' - ' + itemName, async function() {
114
173
  await fetchTestCase(browserContext, testCaseName);
115
- await takeScreenshotAndCompare(browserContext, testCaseName, '#test-app');
174
+ let element = await browserContext.driver.findElement(By.id('test-item-' + itemIndex));
175
+ await scenario(element);
116
176
  });
117
177
  };
118
178
 
@@ -122,9 +182,9 @@ exports.itChecksScreenshot = function(browserContext, testCaseName) {
122
182
  */
123
183
  exports.itChecksScreenshots = function(browserContext, testCaseName, itemNames) {
124
184
  for (let i = 0; i < itemNames.length; ++i) {
125
- it(`${testCaseName} - Item ${i} (${itemNames[i]})`, async function() {
126
- await fetchTestCase(browserContext, testCaseName);
127
- await takeScreenshotAndCompare(browserContext, testCaseName + '/' + i, '#test-item-' + i);
185
+ itCustom(browserContext, testCaseName, i, itemNames[i], async function(element) {
186
+ await takeScreenshot(browserContext, i, element);
187
+ await compareScreenshot(browserContext, i);
128
188
  });
129
189
  }
130
190
  };